java之多線程

多線程

併發和並行

併發:指兩個或多個事件在同一個時間段內發生。java

並行:指兩個或多個事件在同一時刻發生(同時發生)。編程

建立線程

Thread類

 java.lang.Thread 類安全

構造方法

public Thread() :分配一個新的線程對象。多線程

public Thread(String name) :分配一個指定名字的新的線程對象。併發

public Thread(Runnable target) :分配一個帶有指定目標新的線程對象。ide

public Thread(Runnable target,String name) :分配一個帶有指定目標新的線程對象並指定名字。測試

經常使用方法

public void start() :致使此線程開始執行; Java虛擬機調用此線程的run方法。this

  多線程的開啓方式只有調用start方法開啓。spa

public String getName() :獲取當前線程名稱。線程

public static Thread currentThread() :返回對當前正在執行的線程對象的引用。

 1 /*
 2 * 獲取線程的名稱:
 3 *       1,使用Thread類中的getName()方法
 4 *           String getName()  返回該線程的名稱
 5 *       2,能夠先獲取到當前正在執行的線程。使用線程中的方法getName()獲取線程的名稱
 6 *           static Thread currentThread() 返回對當前正在執行的線程對象的引用
 7 * */
 8 
 9 
10 public class MyThread extends Thread {
11 
12     @Override
13     public void run(){
14         // 獲取線程的名稱
15 
16         // String name = getName();
17         // System.out.println(name);
18 
19 
20         // Thread t = Thread.currentThread();
21         // System.out.println(t);
22         //
23         // String name = t.getName();
24         // System.out.println(name);
25 
26 
27         // 鏈式編程
28         System.out.println(Thread.currentThread().getName());
29     }
30 }
31 
32 
33 -------------------------------------------------------------
34 /*
35 * 線程的名稱:
36 *       主線程:main
37 *       新線程:Thread-0,Thread-1
38 * */
39 
40 public class Demo01GetThreadName {
41     public static void main(String[] args) {
42         // 建立Thread類的子類對象
43         MyThread mt = new MyThread();
44 
45         // 調用start方法,開啓新線程執行run方法
46         mt.start();
47 
48         new MyThread().start();
49         new MyThread().start();
50 
51         System.out.println(Thread.currentThread().getName());
52 
53     }
54 }
代碼示例

public static void sleep(long millis) :使當前正在執行的線程以指定的毫秒數暫停(暫時中止執行)。

 1 /*
 2 * public static void sleep(long millis):使當前正在執行的線程以指定的毫秒數暫停(暫時中止執行)。
 3 *       毫秒數結束以後,線程繼續執行
 4 */
 5 
 6 public class Demo01Sleep {
 7     public static void main(String[] args) {
 8         // 模擬秒錶
 9         for(int i =0;i<60;i++){
10 
11             System.out.println(i);
12 
13             // 使用Thread類中的sleep方法讓程序睡眠1秒
14             try {
15                 Thread.sleep(1000);
16             } catch (InterruptedException e) {
17                 e.printStackTrace();
18             }
19         }
20     }
21 }
View Code

第一種方式——建立Thread類的子類

java.long.Thread類:是描述線程的類,咱們想要實現多線程程序,就必須繼承Thread類

實現步驟:

1,建立一個Thread類的子類

2,在Thread類的子類中重寫Thread類中的run方法,設置線程任務(開啓線程要乾的事)

3,建立Thread類的子類對象

4,調用Thread類中的方法start方法,開啓新的線程,執行run方法【start方法自動去子類中找run方法執行】

  void start() 使該線程開始執行;java虛擬機調用該線程的run方法

  結果是兩個線程併發的運行,當前線程(main線程)和另外一個線程(建立的新線程,執行其run方法)。

  屢次啓動一個線程是非法的。特別是當前線程已經結束執行後,不能再從新啓動

 1 //1.建立一個Thread類的子類
 2 public class MyThread extends Thread {
 3 
 4     //2,在Thread類的子類中重寫Thread類中的run方法,設置線程任務(開啓線程要乾的事)
 5     @Override
 6     public void run() {
 7         for (int i = 0; i < 20; i++) {
 8             System.out.println("run" + i);
 9         }
10     }
11 }
12 
13 
14 ---------------------------------------------------------------
15 public class Demo01Thread {
16 
17     public static void main(String[] args) {
18         // 3,建立Thread類的子類對象
19         MyThread my = new MyThread();
20 
21         //4,調用Thread類中的方法start方法,開啓新的線程,執行run方法
22         my.start();
23 
24         //my.start();錯誤的,屢次啓動一個線程非法
25 
26         for (int i = 0; i < 20; i++) {
27             System.out.println("main" + i);
28         }
29     }
30 }
多線程第一種方式代碼實現

注意:java程序屬於搶佔式調度,那個線程優先級高,那個線程就優先執行。同一個優先級就隨機選一個執行

多線程的流程圖解和內存圖解:

第二種方式——實現Runnable接口

java.long.Runnable:Runnable 接口應該由那些打算經過某一線程執行其實例的類來實現。類必須定義一個稱爲 run 的無參數方法。

java.long.Thread類的構造方法

  Thread(Runnable target)分配新的 Thread 對象。

  Thread(Runnable target, String name)分配新的 Thread 對象

實現步驟:

  1,建立一個Runnable接口的實現類

  2,在實現類中重寫Runnable接口中的run方法,設置線程任務。

  3,建立一個Runnable接口的實現類對象

  4,建立一個Thread類對象,參數中傳遞Runnable接口的實現類對象

  5,調用Thread類中的start方法開啓新的線程執行run方法

 1 // 1,建立一個Runnable接口的實現類
 2 public class RunnableImp implements Runnable {
 3 
 4     // 2,在實現類中重寫Runnable接口中的run方法,設置線程任務。
 5     @Override
 6     public void run() {
 7         for (int i = 0; i < 20; i++) {
 8             System.out.println(Thread.currentThread().getName() + i);
 9         }
10     }
11 }
12 
13 
14 -----------------------------------------------------------------
15 public class Dem01Runnable {
16     public static void main(String[] args) {
17         // 3,建立一個Runnable接口的實現類對象
18         RunnableImp run = new RunnableImp();
19 
20         // 4,建立一個Thread類對象,參數中傳遞Runnable接口的實現類對象
21         // Thread t = new Thread(run);// 打印線程名稱
22         Thread t = new Thread(new RunnableImp2());// 打印hello world
23 
24         // 5,調用Thread類中的start方法開啓新的線程執行run方法
25         t.start();
26 
27         for (int i = 0; i < 20; i++) {
28             System.out.println(Thread.currentThread().getName()+i);
29         }
30     }
31 }
實現多線程方式二實現Runnable接口

兩種方式的比較

實現Runnable接口建立多線程的好處:

1,避免了單繼承的侷限性:一個類只能繼承一個類(一我的只能有一個親爹),類繼承了Thread類就不能繼承其餘的類。

    實現了Runnable便可,還能夠繼承其餘的類,實現其餘的接口

2,加強了程序的擴展性,下降了程序的耦合性(解耦):實現Runnable接口的方式,把設置線程任務和開啓新線程進行了分離(解耦)

    1)實現類中,重寫了run方法:用來設置線程任務。

    2)建立Thread類對象,調用start方法:用來開啓多線程。

匿名內部類的方式實現多線程的建立

匿名內部類方法實現線程的建立

  匿名:沒有名字

  內部類:寫在其餘類內部的類

匿名內部類的做用:簡化代碼

  把子類繼承父類,重寫父類的方法,建立子類對象集合一步完成

  把實現類實現接口,重寫接口中的方法,建立實現類對象合成一步完成

匿名內部類的最終產物:子類/實現類對象,而這個類沒有名字

格式:

1 new 父類/接口(){
2     重寫父類/接口中的方法
3 };
 1 public class Demo01InnerClassThread {
 2 
 3     public static void main(String[] args) {
 4         // 線程的父類是Thread
 5         // 原來的步驟:new MyThread子類,調用start方法
 6 
 7         new Thread(){
 8             // 重寫run方法,設置線程任務
 9             @Override
10             public void run(){
11                 for (int i = 0; i < 20; i++) {
12                     System.out.println(Thread.currentThread().getName()+"哈哈哈");
13                 }
14             }
15         }.start();
16 
17 
18 
19 
20         // 線程的接口Runnable
21         // Runnable r = new RunnableImp()
22 
23         Runnable run = new Runnable(){
24             @Override
25             public void run(){
26                 for (int i = 0; i < 20; i++) {
27                     System.out.println(Thread.currentThread().getName()+"嗚嗚嗚");
28                 }
29             }
30         };
31 
32         new Thread(run).start();
33 
34 
35 
36         // 簡化接口的方式
37         new Thread(new Runnable() {
38             @Override
39             public void run() {
40                 for (int i = 0; i < 20; i++) {
41                     System.out.println(Thread.currentThread().getName()+"嗯嗯嗯");
42 
43                 }
44             }
45         }).start();
46 
47     }
48 }
匿名內部類實現多線程代碼示例

線程安全

線程安全

定義:若是有多個線程在同時運行,而這些線程可能會同時運行這段代碼。程序每次運行結果和單線程運行的結果是同樣 的,並且其餘的變量的值也和預期的是同樣的,就是線程安全的。

在多線程中可能出現哪些不安全現象

下面使用一個賣票案例顯示可能出現的問題

 1 /*
 2 實現買票案例
 3  */
 4 public class RunnableImp implements Runnable {
 5     // 定義一個多個線程共享的票源
 6     private int ticket = 100;
 7 
 8     // 設置線程任務:買票
 9     @Override
10     public void run() {
11         // 使用死循環,讓買票操做重複執行
12         while (true){
13             // 判斷票是否存在
14             if (ticket>0){
15                 // 提升安全問題出現的機率,讓程序睡眠
16                 try {
17                     Thread.sleep(10);
18                 } catch (InterruptedException e) {
19                     e.printStackTrace();
20                 }
21 
22 
23                 // 票存在,賣票
24                 System.out.println(Thread.currentThread().getName()+"--->正在賣第"+ticket+"票");
25                 ticket--;
26             }
27         }
28     }
29 }
30 
31 
32 
33 
34 --------------------------------------------------------
35 /*
36 模擬賣票案例:
37     建立三個線程,同時開啓,對共享的票出售
38  */
39 public class Demo01Ticket {
40     public static void main(String[] args) {
41         // 建立Runnable接口的實現類對象
42         RunnableImp run = new RunnableImp();
43 
44         // 一個實現類讓三個線程訪問
45 
46         // 建立Thread類對象,構造方法中傳遞Runnable接口的實現類對象
47         Thread t1 = new Thread(run);
48         Thread t2 = new Thread(run);
49         Thread t3 = new Thread(run);
50 
51         // 開啓多線程
52         t1.start();
53         t2.start();
54         t3.start();
55 
56         // 輸出的有不存在的票和重複的票
57     }
58 }
View Code

輸出結果的部分截圖

發現程序出現了兩個問題:

1. 相同的票數,好比5這張票被賣了兩回。

2. 不存在的票,好比0票與-1票,是不存在的

線程不安全:這種問題,幾個窗口(線程)票數不一樣步了,這種問題稱爲線程不安全

線程同步

當咱們使用多個線程訪問同一資源的時候,且多個線程中對資源有寫的操做,就容易出現線程安全問題。

要解決上述多線程併發訪問一個資源的安全性問題:也就是解決重複票與不存在票問題,Java中提供了同步機制 (synchronized)來解決

線程同步的三種實現方式:

  1. 同步代碼塊。

  2. 同步方法。

  3. 鎖機制。

同步代碼塊

語法:

1  synchronized(鎖對象){
2      可能會出現線程安全問題的代碼(訪問了共享數據的代碼)
3  }

注意:

    1,經過代碼塊中的鎖對象,可使用任意的對象作爲鎖對象

    2,可是必須保證多個線程使用的鎖對象是同一個

    3,鎖對象的做用:把同步代碼塊鎖住,只讓一個線程在同步代碼塊中執行

 1 public class RunnableImp implements Runnable {
 2     // 定義一個多個線程共享的票源
 3     private int ticket = 100;
 4 
 5     // 建立一個鎖對象
 6     Object obj = new Object();
 7 
 8 
 9     // 設置線程任務:買票
10     @Override
11     public void run() {
12         // 使用死循環,讓買票操做重複執行
13         while (true) {
14             // 建立同步代碼塊
15             synchronized (obj) {
16                 // 判斷票是否存在
17                 if (ticket > 0) {
18                     // 提升安全問題出現的機率,讓程序睡眠
19                     try {
20                         Thread.sleep(100);
21                     } catch (InterruptedException e) {
22                         e.printStackTrace();
23                     }
24 
25 
26                     // 票存在,賣票
27                     System.out.println(Thread.currentThread().getName() + "--->正在賣第" + ticket + "票");
28                     ticket--;
29                 }
30             }
31         }
32     }
33 }
34 
35 
36 
37 
38 -------------------------------------------------------------------------------
39 public class Demo01Ticket {
40     public static void main(String[] args) {
41         // 建立Runnable接口的實現類對象
42         RunnableImp run = new RunnableImp();
43 
44         // 一個實現類讓三個線程訪問
45 
46         // 建立Thread類對象,構造方法中傳遞Runnable接口的實現類對象
47         Thread t1 = new Thread(run);
48         Thread t2 = new Thread(run);
49         Thread t3 = new Thread(run);
50 
51         // 開啓多線程
52         t1.start();
53         t2.start();
54         t3.start();
55     }
56 }
View Code

同步方法

格式:

1  修飾符 synchronized 返回值類型 方法名(參數列表){
2      可能出現線程安全問題的代碼(訪問了共享數據的代碼)
3  }

使用步驟:

    1,訪問了共享數據的代碼抽取出來,放在一個方法中

    2,在方法上添加synchronized修飾符

 1 public class RunnableImp implements Runnable {
 2     // 定義一個多個線程共享的票源
 3     private static int ticket = 100;
 4 
 5 
 6     // 設置線程任務:買票
 7     @Override
 8     public void run() {
 9         // 使用死循環,讓買票操做重複執行
10         while (true) {
11             // 判斷票是否存在
12             // payTicket();
13             payTicketStatic();
14         }
15     }
16 
17     // 靜態同步方法
18     /*
19     * 鎖對象是誰?
20     * 不能是this
21     * this是建立對象以後產生的,靜態方法優先於對象
22     * 靜態方法的鎖對象是本類的class屬性--->class文件對象
23     *
24     * */
25 
26     public static synchronized void payTicketStatic() {
27         if (ticket > 0) {
28             // 提升安全問題出現的機率,讓程序睡眠
29             try {
30                 Thread.sleep(10);
31             } catch (InterruptedException e) {
32                 e.printStackTrace();
33             }
34 
35 
36             // 票存在,賣票
37             System.out.println(Thread.currentThread().getName() + "--->正在賣第" + ticket + "票");
38             ticket--;
39         }
40     }
41 
42 
43     // 定義一個同步方法
44     /*
45      * 同步方法也會把方法內部的代碼鎖住
46      * 只讓一個線程執行
47      * 同步方法的鎖對象是誰?
48      * 就是實現類對象  new RunnableImp()
49      * 也就是this
50      *
51      * */
52     public synchronized void payTicket() {
53         if (ticket > 0) {
54             // 提升安全問題出現的機率,讓程序睡眠
55             try {
56                 Thread.sleep(10);
57             } catch (InterruptedException e) {
58                 e.printStackTrace();
59             }
60 
61 
62             // 票存在,賣票
63             System.out.println(Thread.currentThread().getName() + "--->正在賣第" + ticket + "票");
64             ticket--;
65         }
66     }
67 }
68 
69 
70 
71 ------------------------------------------------------------------
72 public class Demo01Ticket {
73     public static void main(String[] args) {
74         // 建立Runnable接口的實現類對象
75         RunnableImp run = new RunnableImp();
76 
77         // 一個實現類讓三個線程訪問
78 
79         // 建立Thread類對象,構造方法中傳遞Runnable接口的實現類對象
80         Thread t1 = new Thread(run);
81         Thread t2 = new Thread(run);
82         Thread t3 = new Thread(run);
83 
84         // 開啓多線程
85         t1.start();
86         t2.start();
87         t3.start();
88 
89         // 輸出的有不存在的票和重複的票
90     }
91 }
View Code

鎖機制(Lock鎖)

java.util.concurrent.locks.lock接口:Lock 實現提供了比使用 synchronized 方法和語句可得到的更普遍的鎖定操做。

Lock接口中的方法:

    void lock() 獲取鎖。

    void unlock() 釋放鎖。

Lock爲接口,因此咱們使用其實現類(ReentrantLock):java.util.concurrent.locks.ReentrantLock implements Lock

使用步驟:

    1,在成員位置建立一個Lock實現類對象ReentrantLock對象

    2,在可能出現安全問題的代碼前調用Lock接口中的方法lock獲取鎖

    3,在可能出現安全問題的代碼後調用Lock接口中的方法unlock釋放鎖

 1 import java.util.concurrent.locks.Lock;
 2 import java.util.concurrent.locks.ReentrantLock;
 3 
 4 public class RunnableImp implements Runnable {
 5     // 定義一個多個線程共享的票源
 6     private int ticket = 100;
 7 
 8     // 1,在成員位置建立一個Lock實現類對象ReentrantLock對象
 9     Lock l = new ReentrantLock();
10 
11 
12 
13     // // 設置線程任務:買票
14     // @Override
15     // public void run() {
16     //     // 使用死循環,讓買票操做重複執行
17     //     while (true){
18     //
19     //         //  2,在可能出現安全問題的代碼前調用Lock接口中的方法lock獲取鎖
20     //         l.lock();
21     //         // 判斷票是否存在
22     //         if (ticket>0){
23     //              // 提升安全問題出現的機率,讓程序睡眠
24     //             try {
25     //                 Thread.sleep(10);
26     //             } catch (InterruptedException e) {
27     //                 e.printStackTrace();
28     //             }
29     //
30     //
31     //             // 票存在,賣票
32     //             System.out.println(Thread.currentThread().getName()+"--->正在賣第"+ticket+"票");
33     //             ticket--;
34     //         }
35     //
36     //         // 3,在可能出現安全問題的代碼後調用Lock接口中的方法unlock釋放鎖
37     //         l.unlock();
38     //     }
39     // }
40 
41 
42     // 更好的寫法
43 
44     // 設置線程任務:買票
45     @Override
46     public void run() {
47         // 使用死循環,讓買票操做重複執行
48         while (true){
49 
50             //  2,在可能出現安全問題的代碼前調用Lock接口中的方法lock獲取鎖
51             l.lock();
52             // 判斷票是否存在
53             if (ticket>0){
54                 // 提升安全問題出現的機率,讓程序睡眠
55                 try {
56                     Thread.sleep(10);
57                     // 票存在,賣票
58                     System.out.println(Thread.currentThread().getName()+"--->正在賣第"+ticket+"票");
59                     ticket--;
60                 } catch (InterruptedException e) {
61                     e.printStackTrace();
62                 } finally {
63                     // 不管程序出現不出現異常,都會把鎖釋放
64                     // 3,在可能出現安全問題的代碼後調用Lock接口中的方法unlock釋放鎖
65                     l.unlock();
66                 }
67             }
68         }
69     }
70 }
71 
72 
73 -----------------------------------------------------------
74 public class Demo01Ticket {
75     public static void main(String[] args) {
76         // 建立Runnable接口的實現類對象
77         RunnableImp run = new RunnableImp();
78 
79         // 一個實現類讓三個線程訪問
80 
81         // 建立Thread類對象,構造方法中傳遞Runnable接口的實現類對象
82         Thread t1 = new Thread(run);
83         Thread t2 = new Thread(run);
84         Thread t3 = new Thread(run);
85 
86         // 開啓多線程
87         t1.start();
88         t2.start();
89         t3.start();
90     }
91 }
View Code

線程狀態

各類狀態之間的互相轉換

等待喚醒機制

Object類中關於線程的方法

多線程中在使用鎖對象的時候,大多數使用Object類來建立對象,由於該對象中有一些關於多線程的方法

Object類中的方法:

     void wait(long timeout) :在其餘線程調用此對象的 notify() 方法或 notifyAll() 方法,或者超過指定的時間量前,致使當前線程等待。

     void wait()在其餘線程調用此對象的 notify() 方法或 notifyAll() 方法前,致使當前線程等待。

     void notify() 喚醒在此對象監視器上等待的單個線程。會執行wait以後的代碼

     void notifyAll() :喚醒在此對象監視器上等待的全部線程。

線程通訊

 1 /*
 2 等待喚醒案例:線程之間的通訊
 3     建立一個顧客線程(消費者):告知老闆要的包子的種類和數量,調用wait方法,放棄CPU的執行,進入到waiting狀態(無限等待)
 4     建立一個老闆線程(消費者):花了5秒作包子,包子作好以後調用notify()方法,喚醒顧客吃包子
 5 
 6 
 7 注意:
 8     顧客和老闆線程必須使用同步代碼塊包裹起來,保證等待和喚醒只能有一個在執行
 9     同步使用的鎖對象必須保證惟一
10     只有鎖對象才能調用wait和notify方法
11  */
12 
13 
14 public class Demo01WaitAndNotify {
15     public static void main(String[] args) {
16         Object obj = new Object();
17 
18         //  建立一個老闆線程(生產者)
19         new Thread() {
20             @Override
21             public void run() {
22                 // 一直作包子
23                 while (true){
24                     // 花5秒作包子
25                     try {
26                         Thread.sleep(5000);
27                     } catch (InterruptedException e) {
28                         e.printStackTrace();
29                     }
30                     // 保證等待和喚醒的線程只能有一個在執行
31                     synchronized (obj) {
32 
33                         System.out.println("老闆5秒中以後作好包子,告知顧客能夠吃包子了");
34                         // 調用wait方法,放棄CPU的執行,進入到waiting狀態(無限等待)
35                         obj.notify();
36                     }
37                 }
38             }
39         }.start();
40 
41 
42         new Thread(){
43             @Override
44             public void run() {
45                 // 一直等着買包子
46                 while (true){
47                     // 保證等待和喚醒的線程只能有一個在執行
48                     synchronized (obj) {
49                         System.out.println("告知老闆要的包子種類和數量");
50                         // 調用wait方法,放棄CPU的執行,進入到waiting狀態(無限等待)
51                         try {
52                             obj.wait();
53                         } catch (InterruptedException e) {
54                             e.printStackTrace();
55                         }
56                         // 喚醒以後執行的代碼
57                         System.out.println("包子作好了,開吃");
58                         System.out.println("---------------------");
59                     }
60                 }
61             }
62         }.start();
63     }
64 }
線程通訊——喚醒鎖對象的一個線程
 1 public class Demo02WaitAndNotify {
 2     public static void main(String[] args) {
 3 
 4         Object obj = new Object();
 5 
 6 
 7         new Thread(){
 8             @Override
 9             public void run() {
10                 // 一直等着買包子
11                 while (true){
12                     // 保證等待和喚醒的線程只能有一個在執行
13                     synchronized (obj) {
14                         System.out.println("顧客1:告知老闆要的包子種類和數量");
15                         // 調用wait方法,放棄CPU的執行,進入到waiting狀態(無限等待)
16                         try {
17                             obj.wait();
18                         } catch (InterruptedException e) {
19                             e.printStackTrace();
20                         }
21                         // 喚醒以後執行的代碼
22                         System.out.println("顧客1:包子作好了,開吃");
23                         System.out.println("---------------------");
24                     }
25                 }
26             }
27         }.start();
28 
29         new Thread(){
30             @Override
31             public void run() {
32                 // 一直等着買包子
33                 while (true){
34                     // 保證等待和喚醒的線程只能有一個在執行
35                     synchronized (obj) {
36                         System.out.println("顧客2:告知老闆要的包子種類和數量");
37                         // 調用wait方法,放棄CPU的執行,進入到waiting狀態(無限等待)
38                         try {
39                             obj.wait();
40                         } catch (InterruptedException e) {
41                             e.printStackTrace();
42                         }
43                         // 喚醒以後執行的代碼
44                         System.out.println("顧客2:包子作好了,開吃");
45                         System.out.println("---------------------");
46                     }
47                 }
48             }
49         }.start();
50 
51 
52         //  建立一個老闆線程(生產者)
53         new Thread() {
54             @Override
55             public void run() {
56                 // 一直作包子
57                 while (true){
58                     // 花5秒作包子
59                     try {
60                         Thread.sleep(5000);
61                     } catch (InterruptedException e) {
62                         e.printStackTrace();
63                     }
64                     // 保證等待和喚醒的線程只能有一個在執行
65                     synchronized (obj) {
66 
67                         System.out.println("老闆5秒中以後作好包子,告知顧客能夠吃包子了");
68                         // 調用wait方法,放棄CPU的執行,進入到waiting狀態(無限等待)
69                         // 若是有多個等待線程隨機喚醒一個等待
70                         //obj.notify();
71 
72                         //若是多個等待的線程喚醒全部
73                         obj.notifyAll();
74                     }
75                 }
76             }
77         }.start();
78     }
79 }
線程通訊——喚醒鎖對象的全部的線程

生產者消費者問題

  1 /*
  2     資源類:包子類
  3     設置包子的屬性
  4   5   6         包子的狀態: 有 true,沒有 false
  7  */
  8 public class BaoZi {
  9     //
 10     String pi;
 11     //
 12     String xian;
 13     //包子的狀態: 有 true,沒有 false,設置初始值爲false沒有包子
 14     boolean flag = false;
 15 
 16 }
 17 
 18 
 19 ------------------------------------------------------------------
 20 /*
 21     消費者(吃貨)類:是一個線程類,能夠繼承Thread
 22     設置線程任務(run):吃包子
 23     對包子的狀態進行判斷
 24     false:沒有包子
 25         吃貨調用wait方法進入等待狀態
 26     true:有包子
 27         吃貨吃包子
 28         吃貨吃完包子
 29         修改包子的狀態爲false沒有
 30         吃貨喚醒包子鋪線程,生產包子
 31  */
 32 public class ChiHuo extends Thread{
 33     //1.須要在成員位置建立一個包子變量
 34     private BaoZi bz;
 35 
 36     //2.使用帶參數構造方法,爲這個包子變量賦值
 37     public ChiHuo(BaoZi bz) {
 38         this.bz = bz;
 39     }
 40     //設置線程任務(run):吃包子
 41     @Override
 42     public void run() {
 43         //使用死循環,讓吃貨一直吃包子
 44         while (true){
 45             //必須同時同步技術保證兩個線程只能有一個在執行
 46             synchronized (bz){
 47                 //對包子的狀態進行判斷
 48                 if(bz.flag==false){
 49                     //吃貨調用wait方法進入等待狀態
 50                     try {
 51                         bz.wait();
 52                     } catch (InterruptedException e) {
 53                         e.printStackTrace();
 54                     }
 55                 }
 56 
 57                 //被喚醒以後執行的代碼,吃包子
 58                 System.out.println("吃貨正在吃:"+bz.pi+bz.xian+"的包子");
 59                 //吃貨吃完包子
 60                 //修改包子的狀態爲false沒有
 61                 bz.flag = false;
 62                 //吃貨喚醒包子鋪線程,生產包子
 63                 bz.notify();
 64                 System.out.println("吃貨已經把:"+bz.pi+bz.xian+"的包子吃完了,包子鋪開始生產包子");
 65                 System.out.println("----------------------------------------------------");
 66             }
 67         }
 68     }
 69 }
 70 
 71 
 72 -------------------------------------------------------------------
 73 /*
 74     生產者(包子鋪)類:是一個線程類,能夠繼承Thread
 75     設置線程任務(run):生產包子
 76     對包子的狀態進行判斷
 77     true:有包子
 78         包子鋪調用wait方法進入等待狀態
 79     false:沒有包子
 80         包子鋪生產包子
 81         增長一些趣味性:交替生產兩種包子
 82             有兩種狀態(i%2==0)
 83         包子鋪生產好了包子
 84         修改包子的狀態爲true有
 85         喚醒吃貨線程,讓吃貨線程吃包子
 86 
 87     注意:
 88         包子鋪線程和包子線程關係-->通訊(互斥)
 89         必須同時同步技術保證兩個線程只能有一個在執行
 90         鎖對象必須保證惟一,可使用包子對象做爲鎖對象
 91         包子鋪類和吃貨的類就須要把包子對象做爲參數傳遞進來
 92             1.須要在成員位置建立一個包子變量
 93             2.使用帶參數構造方法,爲這個包子變量賦值
 94  */
 95 public class BaoZiPu extends Thread{
 96     //1.須要在成員位置建立一個包子變量
 97     private BaoZi bz;
 98 
 99     //2.使用帶參數構造方法,爲這個包子變量賦值
100     public BaoZiPu(BaoZi bz) {
101         this.bz = bz;
102     }
103 
104     //設置線程任務(run):生產包子
105     @Override
106     public void run() {
107         //定義一個變量
108         int count = 0;
109         //讓包子鋪一直生產包子
110         while(true){
111             //必須同時同步技術保證兩個線程只能有一個在執行
112             synchronized (bz){
113                 //對包子的狀態進行判斷
114                 if(bz.flag==true){
115                     //包子鋪調用wait方法進入等待狀態
116                     try {
117                         bz.wait();
118                     } catch (InterruptedException e) {
119                         e.printStackTrace();
120                     }
121                 }
122 
123                 //被喚醒以後執行,包子鋪生產包子
124                 //增長一些趣味性:交替生產兩種包子
125                 if(count%2==0){
126                     //生產 薄皮三鮮餡包子
127                     bz.pi = "薄皮";
128                     bz.xian = "三鮮餡";
129                 }else{
130                     //生產 冰皮 牛肉大蔥陷
131                     bz.pi = "冰皮";
132                     bz.xian = "牛肉大蔥陷";
133 
134                 }
135                 count++;
136                 System.out.println("包子鋪正在生產:"+bz.pi+bz.xian+"包子");
137                 //生產包子須要3秒鐘
138                 try {
139                     Thread.sleep(3000);
140                 } catch (InterruptedException e) {
141                     e.printStackTrace();
142                 }
143                 //包子鋪生產好了包子
144                 //修改包子的狀態爲true有
145                 bz.flag = true;
146                 //喚醒吃貨線程,讓吃貨線程吃包子
147                 bz.notify();
148                 System.out.println("包子鋪已經生產好了:"+bz.pi+bz.xian+"包子,吃貨能夠開始吃了");
149             }
150         }
151     }
152 }
153 
154 
155 --------------------------------------------------------------
156 /*
157     測試類:
158     包含main方法,程序執行的入口,啓動程序
159     建立包子對象;
160     建立包子鋪線程,開啓,生產包子;
161     建立吃貨線程,開啓,吃包子;
162  */
163 public class Demo {
164     public static void main(String[] args) {
165         //建立包子對象;
166         BaoZi bz =new BaoZi();
167         //建立包子鋪線程,開啓,生產包子;
168         new BaoZiPu(bz).start();
169         //建立吃貨線程,開啓,吃包子;
170         new ChiHuo(bz).start();
171     }
172 }
View Code

 

線程池

線程池(JDK1.5以後提供):其實就是一個容納多個線程的容器,其中的線程能夠反覆使用,省去了頻繁建立線程對象的操做,無需反覆建立線程而消耗過多資源。 

 

 

java.util.concurrent.Executors:線程池工程類,用來生產線程池

 

  Executors類中的靜態方法:static ExecutorService newFixedThreadPool(int nThreads):建立一個可重用固定線程數的線程池

    參數:int nThreads:建立線程池中包含的線程數量

    返回值:ExecutorService接口:返回的是ExecutorService接口的實現類對象,可使用ExecutorService接收

java.util.ExecutorService:線程池接口。用來從線程池中獲取線程,調用start方法,執行線程任務

  提交一個Runnable任務用於執行:submit(Runnable task)

  關閉/銷燬 線程池的方法:void shutdown()

使用步驟:

1,使用線程池的工廠類Executors裏面提供的靜態方法newFixedThreadPool生產一個指定線程數量的線程池

2,建立一個類,實現Runnable接口,重寫run方法,設置線程任務

3,調用ExecutorService中的方法submit,傳遞線程任務(實現類),開啓線程執行run方法

4,調用ExecutorService中的方法shutdown銷燬線程池(不建議執行)

 1 public class RunableImpl implements Runnable {
 2 
 3     @Override
 4     public void run(){
 5         System.out.println(Thread.currentThread().getName()+"建立了一個新的線程並執行");
 6     }
 7 }
 8 
 9 
10 ------------------------------------------------------------------
11 import java.util.concurrent.ExecutorService;
12 import java.util.concurrent.Executors;
13 
14 public class Demo01ThreadPool {
15 
16     public static void main(String[] args) {
17         ExecutorService es = Executors.newFixedThreadPool(2);
18 
19         es.submit(new RunableImpl());//pool-1-thread-2建立了一個新的線程並執行
20         es.submit(new RunableImpl());//pool-1-thread-1建立了一個新的線程並執行
21         es.submit(new RunableImpl());//pool-1-thread-1建立了一個新的線程並執行
22         es.submit(new RunableImpl());//pool-1-thread-1建立了一個新的線程並執行
23 
24         //  4,調用ExecutorService中的方法shutdown銷燬線程池(不建議執行)
25         es.shutdown();
26     }
27 }
View Code

 

 

 

 

 

 

 

 

 

 

---------------------------

相關文章
相關標籤/搜索