關於線程的同步,可以使用synchronized關鍵字,或者是使用JDK 5中提供的java.util.concurrent.lock包中的Lock對象。本文探討synchronized關鍵字。
synchronized關鍵字可以修飾方法,可以修飾代碼塊,但不能修飾構造器、屬性等。
對synchronized(this)的一些理解
- 當兩個並發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程得到執行。另一個線程必須等待當前線程執行完這個代碼塊以后才能執行該代碼塊。
- 當一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞。
- 然而,當一個線程訪問object的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該object中的除synchronized(this)同步代碼塊以外的部分。
- 第三個例子同樣適用其它同步代碼塊。也就是說,當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就獲得了這個object的對象鎖。結果,其它線程對該object對象所有同步代碼部分的訪問都被暫時阻塞。
- 以上規則對其它對象鎖同樣適用。
一:synchronized同步代碼塊
package com.clzhang.sample.thread; public class SyncThread1 implements Runnable { private Integer key = 0; @Override public void run() { // key是Integer對象(注意不是int,因為int不是對象) // 線程進入下面同步代碼之前,需要先獲取key的鎖。 // 需要結果是key實現自增長,如果沒有同步塊,則可能會出現重復key值的現象 synchronized (key) { key++; System.out.println(Thread.currentThread().getName() + ":" + key); try { Thread.sleep(100); } catch (InterruptedException e) { } } } public static void main(String[] args) { SyncThread1 st = new SyncThread1(); for(int i=0; i<10; i++) { new Thread(st, "Thread" + i).start(); } } }
輸出:
Thread1:2
Thread3:3
Thread5:4
Thread7:5
Thread0:2
Thread2:7
Thread9:6
Thread4:8
Thread6:9
Thread8:10
二:synchronized同步方法
同步方法分靜態和非靜態兩種。靜態方法則一定會同步,非靜態方法需在單例模式才生效,推薦用靜態方法(不用擔心是否單例)。
2.1 非靜態方法同步示范
package com.clzhang.sample.thread; // 如果是同步方法,則分靜態和非靜態兩種。 // 靜態方法則一定會同步,非靜態方法需在單例模式才生效,推薦用靜態方法(不用擔心是否單例)。 public class SyncThread2 implements Runnable { private Integer key = 0; // 此示范為非靜態方法同步 public synchronized Integer getKey() { key++; return key; } @Override public void run() { System.out.println(Thread.currentThread().getName() + ":" + getKey()); try { Thread.sleep(10); } catch (InterruptedException e) { } } public static void main(String[] args) { // 非靜態方法同步,需要啟動單例模式 SyncThread2 st = new SyncThread2(); for (int i = 0; i < 10; i++) { new Thread(st, "Thread" + i).start(); } } }
輸出:
Thread0:1
Thread1:3
Thread2:2
Thread3:5
Thread5:6
Thread7:7
Thread9:8
Thread6:9
Thread8:10
Thread4:4
2.2 靜態方法同步示范
package com.clzhang.sample.thread; // 如果是同步方法,則分靜態和非靜態兩種。 // 靜態方法則一定會同步,非靜態方法需在單例模式才生效,推薦用靜態方法(不用擔心是否單例)。 public class SyncThread3 implements Runnable { private static Integer key = 0; // 此示范為靜態方法同步 public synchronized static Integer getKey() { key++; return key; } @Override public void run() { System.out.println(Thread.currentThread().getName() + ":" + getKey()); try { Thread.sleep(10); } catch (InterruptedException e) { } } public static void main(String[] args) { // 如果用靜態方法實現同步,則可以生成對象的多個實例 for (int i = 0; i < 10; i++) { SyncThread3 st = new SyncThread3(); new Thread(st, "Thread" + i).start(); } } }
輸出:
Thread3:3
Thread1:1
Thread0:2
Thread5:4
Thread7:5
Thread9:6
Thread2:7
Thread8:10
Thread6:9
Thread4:8
總結
1、無論是同步代碼塊還是同步方法,必須獲得對象鎖才能夠進入同步代碼塊或者同步方法進行操作。
2、如果采用方法級別的同步,對象鎖為方法所在的對象;如果是靜態同步方法,對象鎖為方法所在的類(唯一)。
3、對於代碼塊,對象鎖即指synchronized(object)中的object。
