1. 程式人生 > >java多執行緒物件鎖 類鎖 同步機制詳解

java多執行緒物件鎖 類鎖 同步機制詳解

分享一下我老師大神的人工智慧教程吧。零基礎,通俗易懂!風趣幽默!http://www.captainbed.net/

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

1.在java多執行緒程式設計中物件鎖、類鎖、同步機制synchronized詳解:

    物件鎖:在java中每個物件都有一個唯一的鎖,物件鎖用於物件例項方法或者一個物件例項上面的。

    類鎖:是用於一個類靜態方法或者class物件的,一個類的例項物件可以有多個,但是隻有一個class物件。

    同步機制synchronizedsynchronized關鍵字用於修飾方法或者單獨的synchronized程式碼塊,當一個執行緒想執行synchronized中的內容時,必須先獲取到物件鎖,當物件鎖沒有執行緒佔用時,進入synchronized方法會自動獲取到物件鎖,執行完畢後會自動釋放鎖,如果物件鎖被A執行緒佔用,B執行緒想執行synchronized的程式碼只能等待A個執行緒執行完畢後,釋放物件鎖,B執行緒才能獲取到物件鎖進入方法執行。一個執行緒獲得物件A的鎖,也可以獲得物件B的鎖,兩個不同類的物件鎖沒有關聯。

舉例說明包含synchronized的方法和synchronized程式碼塊

package com.test;public class TestThread public void test1() {  synchronized (this) {   int i = 0;   while (i++<5) {    System.out.println(Thread.currentThread().getName() + " : " + i);    try
{     Thread.sleep(500);    } catch (InterruptedException ie) {    }   }  } } public synchronized void test2() {  int i = 0;    while (i++<5) {   System.out.println(Thread.currentThread().getName() + " : " + i);   try {    Thread.sleep(500);   } catch (InterruptedException ie) {   }  } } public static void main(String[] args) {  final TestThread test = new TestThread();  Thread test1 = new Thread(new Runnable() {   public void run() {    test.test1();   }  }, "test1");  Thread test2 = new Thread(new Runnable() {   public void run() {    test.test2();   }  }, "test2");  test1.start();  test2.start(); }}


以上程式碼執行結果如下:

以上程式碼是因為執行同一個物件,所以另一個執行緒要等前一個物件,執行完成後釋放掉物件鎖,拿到物件鎖才能繼續執行synchronized裡面的東西。

如果我們去掉synchronized修飾的方法或者synchronized程式碼塊,將會打印出以下的結果:


如何要是去掉一個synchronized後,輸出的語句是交叉執行的。這就說明,對於同一個物件,如果執行緒A得到了物件鎖,執行緒B可以訪問物件沒有同步的方法和程式碼。進行同步的程式碼和沒有同步的程式碼是互不影響的。

舉例類鎖:

package com.test;public class MyThreadClass public static void main(String[] args) {  final MyThreadClass my=new MyThreadClass();  Thread thread1=new Thread(new Runnable() {   @Override   public void run() {    try {     my.test1();    } catch (InterruptedException e) {     e.printStackTrace();    }   }  },"test1");  Thread thread2=new Thread(new Runnable() {   @Override   public void run() {    try {     MyThreadClass.test2();    } catch (InterruptedException e) {     e.printStackTrace();    }   }  },"test2");  thread1.start();  thread2.start(); }   public void test1() throws InterruptedException{  synchronized (MyThreadClass.class) {   int i=0;   while(i++<5){    System.out.println(Thread.currentThread().getName()+":"+i);    Thread.sleep(500);   }  } }  public static synchronized void test2() throws InterruptedException{   int i=0;   while(i++<5){    System.out.println(Thread.currentThread().getName()+":"+i);    Thread.sleep(500);   } }} 
執行結果:



類鎖和物件鎖其實是一樣的,只是針對於不同的物件。

如果兩個方法一個被synchronized修飾,一個靜態方法被synchronized修飾(體現類鎖和物件鎖的區別),程式碼如下:

package com.test;public class MyThreadClass2 public static void main(String[] args) {  final MyThreadClass2 my=new MyThreadClass2();  Thread thread1=new Thread(new Runnable() {   @Override   public void run() {    try {     my.test1();    } catch (InterruptedException e) {     e.printStackTrace();    }   }  },"test1");  Thread thread2=new Thread(new Runnable() {   @Override   public void run() {    try {     MyThreadClass2.test2();    } catch (InterruptedException e) {     e.printStackTrace();    }   }  },"test2");  thread1.start();  thread2.start(); }  public synchronized void test1() throws InterruptedException{   int i=0;   while(i++<5){    System.out.println(Thread.currentThread().getName()+":"+i);    Thread.sleep(500);   } }  public static synchronized void test2() throws InterruptedException{   int i=0;   while(i++<5){    System.out.println(Thread.currentThread().getName()+":"+i);    Thread.sleep(500);   } }} 
程式碼執行結果如下:


結果也是交叉輸出的,雖然都是被synchronized修飾,但是一個是屬於物件的,一個是屬於class。所以類鎖和物件鎖是不同的,他們控制著不同的區域,互不干擾。

疑問:java中既然synchronized修飾的方法和synchronized程式碼塊作用是一樣的,為什麼還需要synchronized程式碼塊呢?

使用synchronized修飾方式是直接在方法上面加鎖,synchronized方法塊是在方法裡面加鎖,一個範圍大,一個範圍小。還有一個最主要的在應用場景如下:在Class中建立一個物件,在Class中要執行這個物件的某個方法,為了防止多個執行緒同時執行,採用同步加鎖,但是如果這個方法出現死迴圈或者執行時間很長,其他執行緒也不能執行物件的其他同步方法,需要等待這個執行緒執行完畢,影響系統性能。如果採用synchronized修飾方法和synchronized程式碼塊可能都會出現這種情況,我們來模擬一下這種狀況:

建立一個java物件類,裡面有兩個方法:

package com.test;public class Test public void test1(){ } public void test2(){ }}
建立測試類程式碼如下:

package com.test;public class MyThreadClass4 static Test test=new Test(); public static void main(String[] args) {  final MyThreadClass4 my=new MyThreadClass4();  Thread thread1=new Thread(new Runnable() {   @Override   public void run() {    try {     my.test1();    } catch (InterruptedException e) {     e.printStackTrace();    }   }  },"test1");  Thread thread2=new Thread(new Runnable() {   @Override   public void run() {    try {     my.test2();    } catch (InterruptedException e) {     e.printStackTrace();    }   }  },"test2");  thread1.start();  thread2.start(); } long startTime; public synchronized void test1() throws InterruptedException{   test.test1();   System.out.println(Thread.currentThread().getName());   startTime = System.currentTimeMillis();//獲取當前時間   Thread.sleep(5000); }  public synchronized void test2() throws InterruptedException{  long endTime = System.currentTimeMillis();  test.test2();  System.out.println(Thread.currentThread().getName());  System.out.println("程式執行時間:"+(endTime-startTime)+"ms"); }} 

在class裡面建立物件例項,然後將兩個方法分別呼叫,在呼叫方法上面都加上synchronized修飾方法Thread.sleep(5000)來模擬方法執行時間過長或者死迴圈,但是可以看到時間為我們設定的時間,執行結果不理想執行結果如下:


如果我們採用synchronized程式碼塊的方式如下:

package com.test;public class MyThreadClass3 static Test test=new Test(); public static void main(String[] args) {  final MyThreadClass3 my=new MyThreadClass3();  Thread thread1=new Thread(new Runnable() {   @Override   public void run() {    try {     my.test1();    } catch (InterruptedException e) {     e.printStackTrace();    }   }  },"test1");  Thread thread2=new Thread(new Runnable() {   @Override   public void run() {    try {     my.test2();    } catch (InterruptedException e) {     e.printStackTrace();    }   }  },"test2");  thread1.start();  thread2.start(); } long startTime; public void test1() throws InterruptedException{  synchronized (test) {   test.test1();   System.out.println(Thread.currentThread().getName());   startTime = System.currentTimeMillis();//獲取當前時間   Thread.sleep(5000);  } }  public synchronized void test2() throws InterruptedException{  long endTime = System.currentTimeMillis();  test.test2();  System.out.println(Thread.currentThread().getName());  System.out.println("程式執行時間:"+(endTime-startTime)+"ms"); }} 
synchronized程式碼塊中我們只是對當前test物件加鎖,和執行這塊程式碼的物件沒有任何關係。執行test1的同時,我照樣可以執行其他的synchronized同步方法,增強系統性能。執行結果如下:

           

給我老師的人工智慧教程打call!http://www.captainbed.net/

這裡寫圖片描述