1. 程式人生 > >201771010108 -韓臘梅-第十七週學習總結

201771010108 -韓臘梅-第十七週學習總結

第十七週學習總結

 

一、知識總結

1.建立執行緒的2種方法

方式1:繼承java.lang.Thread類,並覆蓋run()方法。優勢:編寫簡單;劣勢:無法繼承其他父類

方式2:實現java.lang.Runnable介面,並實現run()方法。優勢:可以繼承其他類,多執行緒可以共享同一個Thread物件;劣勢:程式設計方式稍微複雜,如需訪問當前執行緒,需呼叫Thread.currentThread()方法

2. Java建立執行緒後,呼叫start()方法和run()的區別

兩種方法的區別

1) start:

    用start方法來啟動執行緒,真正實現了多執行緒執行,這時無需等待run方法體程式碼執行完畢而直接繼續執行下面的程式碼。通過呼叫Thread類的start()方法來啟動一個執行緒,這時此執行緒處於就緒(可執行)狀態,並沒有執行,一旦得到cpu時間片,就開始執行run()方法,這裡方法run()稱為執行緒體,它包含了要執行的這個執行緒的內容,Run方法執行結束,此執行緒隨即終止。

2) run:

    run()方法只是類的一個普通方法而已,如果直接呼叫run方法,程式中依然只有主執行緒這一個執行緒,其程式執行路徑還是隻有一條,還是要順序執行,還是要等待

run方法體執行完畢後才可繼續執行下面的程式碼,這樣就沒有達到寫執行緒的目的。

總結:呼叫start方法方可啟動執行緒,而run方法只是thread的一個普通方法呼叫,還是在主執行緒裡執行。

這兩個方法應該都比較熟悉,把需要並行處理的程式碼放在run()方法中,start()方法啟動執行緒將自動呼叫 run()方法,這是由jvm的記憶體機制規定的。並且run()方法必須是public訪問許可權,返回值型別為void。

兩種方式的比較 :

實際中往往採用實現Runable介面,一方面因為java只支援單繼承,繼承了Thread類就無法再繼續繼承其它類,而且Runable介面只有一個run方法;另一方面通過結果可以看出實現Runable接口才是真正的多執行緒。

3.執行緒的生命週期

執行緒是一個動態執行的過程,它也有一個從生產到死亡的過程。

(1)生命週期的五種狀態

新建(new Thread)

當建立Thread類的一個例項(物件)時,此執行緒進入新建狀態(未被啟動)。

例如:Thread t1 = new Threade();

就緒(runnable)

執行緒已經被啟動(start),正在等待分配CPU時間片,也就是說此事執行緒正在就緒佇列中排隊等候得到CPU資源。

例如:t1.start();

執行(running)

執行緒獲得cpuz資源正在執行任務(run()方法),此時除非執行緒自動放棄CPU資源或者有優先順序更高的的執行緒進入,執行緒將一直執行到結束!

死亡(dead)

當執行緒執行完畢或被其它執行緒殺死,執行緒就進入死亡狀態,這時執行緒不可能再進入就緒狀態等待執行。

自然終止:正常執行run()方法後終止

異常終止:呼叫stop()方法讓一個執行緒終止執行

堵塞(blocked)

由於某種原因導致正在執行的執行緒讓出CPU並暫停自己的執行,即進入堵塞狀態。

正在睡眠:用sleep(long t) 方法可使執行緒進入睡眠方式。一個睡眠著的執行緒在指定的時間過去可進入就緒狀態。

正在等待:呼叫wait()方法。(呼叫notify()方法回到就緒狀態)

被另一個執行緒所阻塞:呼叫suspend()方法。(呼叫resume()方法恢復)

5.如何實現執行緒同步?

當多個執行緒訪問同一個資料時,容易出現執行緒安全問題,需要某種方式來確保資源在某一時刻只被一個執行緒使用。需要讓執行緒同步,保證資料安全

執行緒同步的實現方案:同步程式碼塊和同步方法,均需要使用synchronized關鍵字

執行緒同步的好處:解決了執行緒安全問題

執行緒同步的缺點:效能下降,可能會帶來死鎖

6. 關於同步鎖的更多細節

Java中每個物件都有一個內建鎖。

當程式執行到非靜態的synchronized同步方法上時,自動獲得與正在執行程式碼類的當前例項(this例項)有關的鎖。獲得一個物件的鎖也稱為獲取鎖、鎖定物件、在物件上鎖定或在物件上同步。

當程式執行到synchronized同步方法或程式碼塊時才該物件鎖才起作用。

一個物件只有一個鎖。所以,如果一個執行緒獲得該鎖,就沒有其他執行緒可以獲得鎖,直到第一個執行緒釋放(或返回)鎖。這也意味著任何其他執行緒都不能進入該物件上的synchronized方法或程式碼塊,直到該鎖被釋放。

釋放鎖是指持鎖執行緒退出了synchronized同步方法或程式碼塊。

關於鎖和同步,有一下幾個要點:

1)、只能同步方法,而不能同步變數和類;

2)、每個物件只有一個鎖;當提到同步時,應該清楚在什麼上同步?也就是說,在哪個物件上同步?

3)、不必同步類中所有的方法,類可以同時擁有同步和非同步方法。

4)、如果兩個執行緒要執行一個類中的synchronized方法,並且兩個執行緒使用相同的例項來呼叫方法,那麼一次只能有一個執行緒能夠執行方法,另一個需要等待,直到鎖被釋放。也就是說:如果一個執行緒在物件上獲得一個鎖,就沒有任何其他執行緒可以進入(該物件的)類中的任何一個同步方法。

5)、如果執行緒擁有同步和非同步方法,則非同步方法可以被多個執行緒自由訪問而不受鎖的限制。

6)、執行緒睡眠時,它所持的任何鎖都不會釋放。

7)、執行緒可以獲得多個鎖。比如,在一個物件的同步方法裡面呼叫另外一個物件的同步方法,則獲取了兩個物件的同步鎖。

8)、同步損害併發性,應該儘可能縮小同步範圍。同步不但可以同步整個方法,還可以同步方法中一部分程式碼塊。

9)、在使用同步程式碼塊時候,應該指定在哪個物件上同步,也就是說要獲取哪個物件的鎖。例如:

    public int fix(int y) {

        synchronized (this) {

            x = x - y;

        }

        return x;

    }

當然,同步方法也可以改寫為非同步方法,但功能完全一樣的,例如:

    public synchronized int getX() {

        return x++;

    }

    public int getX() {

        synchronized (this) {

            return x;

        }

    }

效果是完全一樣的。

7. 簡述sleep( )和wait( )有什麼區別?

sleep()是讓某個執行緒暫停執行一段時間,其控制範圍是由當前執行緒決定,也就是說,線上程裡面決定.好比如說,我要做的事情是 "點火->燒水->煮麵",而當我點完火之後我不立即燒水,我要休息一段時間再燒.對於執行的主動權是由我的流程來控制。

而wait(),首先,這是由某個確定的物件來呼叫的,將這個物件理解成一個傳話的人,當這個人在某個執行緒裡面說"暫停!",也是 thisObj.wait(),這裡的暫停是阻塞,還是"點火->燒水->煮飯",thisObj就好比一個監督我的人站在我旁邊,本來該線 程應該執行1後執行2,再執行3,而在2處被那個物件喊暫停,那麼我就會一直等在這裡而不執行3,但正個流程並沒有結束,我一直想去煮飯,但還沒被允許, 直到那個物件在某個地方說"通知暫停的執行緒啟動!",也就是thisObj.notify()的時候,那麼我就可以煮飯了,這個被暫停的執行緒就會從暫停處 繼續執行。
其實兩者都可以讓執行緒暫停一段時間,但是本質的區別是一個執行緒的執行狀態控制,一個是執行緒之間的通訊的問題。

二、實驗部分——執行緒同步控制

1、實驗目的與要求

 

(1) 掌握執行緒同步的概念及實現技術;

 

(2) 執行緒綜合程式設計練習

 

2、實驗內容和步驟

 

實驗1:測試程式並進行程式碼註釋。

 

測試程式1:

 

l  在Elipse環境下除錯教材651頁程式14-7,結合程式執行結果理解程式;

 

l  掌握利用鎖物件和條件物件實現的多執行緒同步技術。

package synch;

import java.util.*;
import java.util.concurrent.locks.*;

/**
一個銀行有許多銀行帳戶,使用鎖序列化訪問 * @version 1.30 2004-08-01
 * @author Cay Horstmann
 */
public class Bank
{
   private final double[] accounts;
   private Lock bankLock;
   private Condition sufficientFunds;

   /**
    * 建設銀行。
    * @param n 賬號
    * @param initialBalance 每個賬戶的初始餘額
    */
   public Bank(int n, double initialBalance)
   {
      accounts = new double[n];
      Arrays.fill(accounts, initialBalance);
      bankLock = new ReentrantLock();
      sufficientFunds = bankLock.newCondition();
   }

   /**
    * 把錢從一個賬戶轉到另一個賬戶。
    * @param 從賬戶轉賬
    * @param 轉到要轉賬的賬戶
    * @param 請允許我向你轉達
    */
   public void transfer(int from, int to, double amount) throws InterruptedException
   {
      bankLock.lock();
      try
      {
         while (accounts[from] < amount)
            sufficientFunds.await();
         System.out.print(Thread.currentThread());
         accounts[from] -= amount;
         System.out.printf(" %10.2f from %d to %d", amount, from, to);
         accounts[to] += amount;
         System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
         sufficientFunds.signalAll();
      }
      finally
      {
         bankLock.unlock();
      }
   }

   /**
    * 獲取所有帳戶餘額的總和。
    * @return 總餘額
    */
   public double getTotalBalance()
   {
      bankLock.lock();
      try
      {
         double sum = 0;

         for (double a : accounts)
            sum += a;

         return sum;
      }
      finally
      {
         bankLock.unlock();
      }
   }

   /**
    * 獲取銀行中的帳戶數量。
    * @return 賬號
    */
   public int size()
   {
      return accounts.length;
   }
}
Bank
package synch;

/**
 * 這個程式顯示了多個執行緒如何安全地訪問資料結構。
 * @version 1.31 2015-06-21
 * @author Cay Horstmann
 */
public class SynchBankTest
{
   public static final int NACCOUNTS = 100;
   public static final double INITIAL_BALANCE = 1000;
   public static final double MAX_AMOUNT = 1000;
   public static final int DELAY = 10;
   
   public static void main(String[] args)
   {
      Bank bank = new Bank(NACCOUNTS, INITIAL_BALANCE);
      for (int i = 0; i < NACCOUNTS; i++)
      {
         int fromAccount = i;
         Runnable r = () -> {
            try
            {
               while (true)
               {
                  int toAccount = (int) (bank.size() * Math.random());
                  double amount = MAX_AMOUNT * Math.random();
                  bank.transfer(fromAccount, toAccount, amount);
                  Thread.sleep((int) (DELAY * Math.random()));
               }
            }
            catch (InterruptedException e)
            {
            }            
         };
         Thread t = new Thread(r);
         t.start();
      }
   }
}
SynchBankTest

 

測試程式2:

 

l  在Elipse環境下除錯教材655頁程式14-8,結合程式執行結果理解程式;

 

l  掌握synchronized在多執行緒同步中的應用。

package synch2;

import java.util.*;

/**
 * 具有多個使用同步原語的銀行賬戶的銀行。
 * @version 1.30 2004-08-01
 * @author Cay Horstmann
 */
public class Bank
{
   private final double[] accounts;

   /**
    * 建設銀行。
    * @param n 賬號
    * @param initialBalance 每個賬戶的初始餘額
    */
   public Bank(int n, double initialBalance)
   {
      accounts = new double[n];
      Arrays.fill(accounts, initialBalance);
   }

   /**
    * 把錢從一個賬戶轉到另一個賬戶。
    * @param 從賬戶轉賬
    * @param 轉到要轉賬的賬戶
    * @param 請允許我向你轉達
    */
   public synchronized void transfer(int from, int to, double amount) throws InterruptedException
   {
      while (accounts[from] < amount)
         wait();
      System.out.print(Thread.currentThread());
      accounts[from] -= amount;
      System.out.printf(" %10.2f from %d to %d", amount, from, to);
      accounts[to] += amount;
      System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
      notifyAll();
   }

   /**
    * 獲取所有帳戶餘額的總和。
    * @return 總餘額
    */
   public synchronized double getTotalBalance()
   {
      double sum = 0;

      for (double a : accounts)
         sum += a;

      return sum;
   }

   /**
    * 獲取銀行中的帳戶數量。
    * @return 
    */
   public int size()
   {
      return accounts.length;
   }
}
Bank
package synch2;

/**
 * 
 * 這個程式展示了多個執行緒如何使用同步方法安全地訪問資料結構。
 * @version 1.31 2015-06-21
 * @author Cay Horstmann
 */
public class SynchBankTest2
{
   public static final int NACCOUNTS = 100;
   public static final double INITIAL_BALANCE = 1000;
   public static final double MAX_AMOUNT = 1000;
   public static final int DELAY = 10;

   public static void main(String[] args)
   {
      Bank bank = new Bank(NACCOUNTS, INITIAL_BALANCE);
      for (int i = 0; i < NACCOUNTS; i++)
      {
         int fromAccount = i;
         Runnable r = () -> {
            try
            {
               while (true)
               {
                  int toAccount = (int) (bank.size() * Math.random());
                  double amount = MAX_AMOUNT * Math.random();
                  bank.transfer(fromAccount, toAccount, amount);
                  Thread.sleep((int) (DELAY * Math.random()));
               }
            }
            catch (InterruptedException e)
            {
            }
         };
         Thread t = new Thread(r);
         t.start();
      }
   }
}
SynchBankTest2

 

測試程式3:

 

l  在Elipse環境下執行以下程式,結合程式執行結果分析程式存在問題;

 

l  嘗試解決程式中存在問題。

 

class Cbank

{

     private static int s=2000;

     public   static void sub(int m)

     {

           int temp=s;

           temp=temp-m;

          try {

                     Thread.sleep((int)(1000*Math.random()));

                   }

           catch (InterruptedException e)  {              }

                 s=temp;

                 System.out.println("s="+s);

             }

}

 

 

class Customer extends Thread

{

  public void run()

  {

   for( int i=1; i<=4; i++)

     Cbank.sub(100);

    }

 }

public class Thread3

{

 public static void main(String args[])

  {

   Customer customer1 = new Customer();

   Customer customer2 = new Customer();

   customer1.start();

   customer2.start();

  }

}

 

執行結果:

改正程式碼:

class Cbank
{
     private static int s=2000;
     public  synchronized static void sub(int m)
     {
           int temp=s;
           temp=temp-m;
          try {
                 Thread.sleep((int)(1000*Math.random()));
               }
           catch (InterruptedException e)  {              }
              s=temp;
              System.out.println("s="+s);
          }
    }


class Customer extends Thread
{
  public void run()
  {
   for( int i=1; i<=4; i++)
     Cbank.sub(100);
    }
 }

public class Thread3
{
 public static void main(String args[])
  {
   Customer customer1 = new Customer();
  
   Customer customer2 = new Customer();
   customer1.start();
   customer2.start();
  }
}
Cbank

實驗2 程式設計練習

 

利用多執行緒及同步方法,編寫一個程式模擬火車票售票系統,共3個視窗,賣10張票,程式輸出結果類似(程式輸出不唯一,可以是其他類似結果)。

 

Thread-0視窗售:第1張票

 

Thread-0視窗售:第2張票

 

Thread-1視窗售:第3張票

 

Thread-2視窗售:第4張票

 

Thread-2視窗售:第5張票

 

Thread-1視窗售:第6張票

 

Thread-0視窗售:第7張票

 

Thread-2視窗售:第8張票

 

Thread-1視窗售:第9張票

 

Thread-0視窗售:第10張票

 

程式碼:

package 程式設計一;

public class 售票 {
public static void main(String[] args) {
mythread mythread=new mythread();
Thread t1=new Thread(mythread);
Thread t2=new Thread(mythread);
Thread t3=new Thread(mythread);
t1.start();
t2.start();
t3.start();
}
}
class mythread implements Runnable{
int t=1;
boolean flag=true;
@Override
public void run() {
while(flag) {
try {
Thread.sleep(50);
}catch(InterruptedException e)
{
e.printStackTrace();
}
synchronized (this) {
if(t<=10) {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName() + "視窗售:第" + t + "張票");
t++;
}
if(t>10) {
flag=false;
}
}
}

}

}

 
售票

結果:

三、實驗總結

         這是本學期最後一次實驗,實驗內容也比較少,對執行緒有了更深的認識,很感謝學長和老師這一學期幫助。