1. 程式人生 > >java Thread源碼分析(二)

java Thread源碼分析(二)

except override int throw enter syn trace zed lis

一、sleep的使用

技術分享圖片
 1 public class ThreadTest {
 2     public static void main(String[] args) throws InterruptedException {
 3         Object obj = new Object();
 4         MyThread mt = new MyThread(obj);
 5         mt.start();
 6         MyThread mt2 = new MyThread(obj);
 7         mt2.start();
8 } 9 private static class MyThread extends Thread{ 10 private Object obj; 11 public MyThread(Object obj) { 12 this.obj = obj; 13 } 14 @Override 15 public void run() { 16 System.out.println(Thread.currentThread().getName() + 17
" synchronized之前: " + System.currentTimeMillis()); 18 synchronized(obj) { 19 System.out.println(Thread.currentThread().getName() + 20 " sleep之前: " + System.currentTimeMillis()); 21 try { 22 Thread.sleep(2000);
23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } 26 System.out.println(Thread.currentThread().getName() + 27 " sleep之後: " + System.currentTimeMillis()); 28 } 29 } 30 } 31 }
View Code

輸出:

Thread-1 synchronized之前: 1546337474050
Thread-0 synchronized之前: 1546337474050
Thread-1 sleep之前: 1546337474051
Thread-1 sleep之後: 1546337476051
Thread-0 sleep之前: 1546337476051
Thread-0 sleep之後: 1546337478052

線程Thread-0和線程Thread-1監控同一個資源obj,Thread-1在sleep之後並沒有釋放對象鎖。

好比,A和B去店裏買衣服,只有一間試衣間,一把試衣間的鑰匙,A先拿到試衣間鑰匙(obj),進入試衣間(synchronized(obj) {...}),

並在試衣間睡了一覺(sleep(2000)),B只能等A醒來走出試衣間,才能有機會拿到鑰匙並進入試衣間。

調用native方法:public static native void sleep(long millis) throws InterruptedException;

二、wait和notify的使用

技術分享圖片
 1 public class ThreadTest {
 2     public static void main(String[] args) throws InterruptedException {
 3         Object obj = new Object();
 4         MyThread mt = new MyThread(obj);
 5         mt.start();
 6         MyThread2 mt2 = new MyThread2(obj);
 7         mt2.start();
 8     }
 9     private static class MyThread extends Thread{
10         private Object obj;
11         public MyThread(Object obj) {
12             this.obj = obj;
13         }
14         @Override
15         public void run() {
16             System.out.println(Thread.currentThread().getName() + 
17                     " synchronized之前: " + System.currentTimeMillis());
18             synchronized(obj) {
19                 System.out.println(Thread.currentThread().getName() + 
20                         " wait之前: " + System.currentTimeMillis());
21                 try {
22                     obj.wait();
23                 } catch (InterruptedException e) {
24                     // TODO Auto-generated catch block
25                     e.printStackTrace();
26                 }
27                 System.out.println(Thread.currentThread().getName() + 
28                         " wait之後: " + System.currentTimeMillis());
29             }
30         }
31     }
32     private static class MyThread2 extends Thread{
33         private Object obj;
34         public MyThread2(Object obj) {
35             this.obj = obj;
36         }
37         @Override
38         public void run() {
39             System.out.println(Thread.currentThread().getName() + 
40                     " synchronized之前: " + System.currentTimeMillis());
41             synchronized(obj) {
42                 System.out.println(Thread.currentThread().getName() + 
43                         " notify之前: " + System.currentTimeMillis());
44                 obj.notify();
45                 System.out.println(Thread.currentThread().getName() + 
46                         " notify之後: " + System.currentTimeMillis());
47             }
48         }
49     }
50 }
View Code

輸出:

Thread-0 synchronized之前: 1546349737274
Thread-0 wait之前: 1546349737274
Thread-1 synchronized之前: 1546349737274
Thread-1 notify之前: 1546349737275
Thread-1 notify之後: 1546349737275
Thread-0 wait之後: 1546349737275

Thread-0在執行了obj.wait()之後,線程暫停並釋放對象鎖,之後,Thread-1獲得對象鎖,Thread-1執行obj.notify()後Thread-0蘇醒,Thread-1執行完synchronized的代碼塊之後,Thread-0才有機會獲得鎖。

1、wait()讓當前線程進入“等待狀態”,並讓當前線程釋放它所持有的鎖。直到其他線程調用此對象的notify()方法或notify()方法,當前線程被喚醒,進入“就緒狀態”。

2、notify()和notifyAll()的作用,則是喚醒當前對象上的等待線程。notify()是喚醒單個線程(隨機喚醒),而notifyAll()是喚醒所有的線程。

3、wait(long timeout)讓當前線程處於“等待(阻塞)狀態”,直到其他線程調用此對象的notify()方法或notifyAll()方法,或者超過指定的時間量,當前線程被喚醒,進入“就緒狀態”。

4、調用 wait()、notify()、notifyAll() 方法之前,必須獲得對象鎖,即,只能在同步方法中調用。

5、執行 notify() 之後,並不會立即退出讓wait的線程執行,必須要先將同步塊中的程序執行完,退出同步塊,才會釋放鎖,讓等待線程執行。

6、每調用一次 notify() 只能喚醒一個線程,多次調用可通知多個線程。

wait()、notify()、notifyAll()都是Object的方法。

原理:

每個對象都有個monitor,初始是0,執行完synchronized值就是1。

wait/notify需要在獲得monitor的線程中才可以執行。

所以,wait/notify需要在synchronized中執行。

其中,wait又會釋放掉鎖,破壞掉同步。

和synchronized的關系:

synchronized代碼塊生成的字節碼,被monitorenter和monitorexit包圍,持有對象的monitor,線程執行wait/notify方法時,必須持有對象的monitor,所以,wait/notify方法在synchronized同步快中執行,就持有了對象的鎖。

互斥和協同:

java語言的同步機制在底層實現上就只有兩種方式:互斥和協同。

互斥:即synchronized內置鎖。

協同:即內置條件隊列,wait/notify/notyfiAll。

條件隊列是處於等待狀態的線程,等待特定條件為真。每個java對象都可以作為一個鎖,同樣每個java對象都可以作為一個條件隊列。通過wait/notify/notifyAll來操作條件隊列。

可以理解為:有一個隊列,o.wait()就push進去,o.notify()就pull出來。、

要調用條件隊列的任何一個方法,都必須要獲得對象上的鎖。

三、join的使用

技術分享圖片
 1 public class ThreadTest {
 2     public static void main(String[] args) throws InterruptedException {
 3         Object obj = new Object();
 4         MyThread mt = new MyThread(obj);
 5         mt.start();
 6         //在main中調用mt.join()
 7         mt.join();    
 8         System.out.println("main");
 9     }
10     private static class MyThread extends Thread{
11         private Object obj;
12         public MyThread(Object obj) {
13             this.obj = obj;
14         }
15         @Override
16         public void run() {
17             System.out.println(Thread.currentThread().getName() + 
18                     " synchronized之前: " + System.currentTimeMillis());
19             synchronized(obj) {
20                 System.out.println(Thread.currentThread().getName() + 
21                         " sleep之前: " + System.currentTimeMillis());
22                 try {
23                     Thread.sleep(3000);
24                 } catch (InterruptedException e) {
25                     // TODO Auto-generated catch block
26                     e.printStackTrace();
27                 }
28                 System.out.println(Thread.currentThread().getName() + 
29                         " sleep之後: " + System.currentTimeMillis());
30             }
31         }
32     }
33 }
View Code

輸出:

Thread-0 synchronized之前: 1546354871560
Thread-0 sleep之前: 1546354871560
Thread-0 sleep之後: 1546354874561
main

在main中調用了線程mt的join方法(mt.join()),則線程main會等線程mt執行完畢後再恢復運行。

join()方法由wait()實現。源碼:

java Thread源碼分析(二)