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

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

bool try 老師 length 同步原語 font pla 輸出 學習總結

第十七周學習總結

一、知識總結

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;
}
}
}

}

}

 
售票

結果:

技術分享圖片

三、實驗總結

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

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