【轉(zhuǎn)】使用synchronized進(jìn)行Java線程同步
線程同步指多個(gè)線程同時(shí)訪問某資源時(shí),采用一系列的機(jī)制以保證同時(shí)最多只能一個(gè)線程訪問該資源。為什么需要線程同步呢?
我們舉一個(gè)最簡(jiǎn)單的例子來說明為什么需要線程同步。
比如有一本書(有且只有一本),交給多個(gè)售貨員同時(shí)去賣;
如果其中任何一個(gè)售貨員把這本書給賣了,其他售貨員就不能再賣這本書了。
現(xiàn)實(shí)生活中,如果要保證該書不會(huì)被多個(gè)售貨員同時(shí)賣掉,必須要有一種機(jī)制來保證:
比如,售貨員應(yīng)該拿到該書之后才能開始賣書,暫時(shí)拿不到的話就只能等該書被退回柜臺(tái)。
售書的完整的例子可以參考 范例解說Java里的線程概念與線程同步技術(shù) 一文
這里,每一個(gè)售貨員售書可以看作一個(gè)線程。欲售的書便是各線程需要共享的資源。
開始售書之前,需要取得該書(資源),取不到情況下等待:資源取得
開始售書之后,則需要取得對(duì)該書的獨(dú)享控制(不讓他人拿到該書):資源加鎖
售完書時(shí),需要通知柜臺(tái)該書已售出;或者未售出時(shí),把書退回柜臺(tái)(通知他人可以拿到該書):資源解鎖
synchronized控制線程同步的概念跟此完全一樣。
Java里可以使用synchronized來同步代碼塊或者方法。
同步代碼塊例:
- synchronized(欲同步的對(duì)象obj) {
- 需要同步的代碼塊
- }
可以同步代碼塊。
synchronized (obj) 表示若多個(gè)線程同時(shí)訪問時(shí),只讓其中一個(gè)線程最先取得obj對(duì)象并對(duì)其加鎖,其它線程則阻塞直到取得obj對(duì)象的線程執(zhí)行完代碼塊,此時(shí)被加鎖的obj對(duì)象得到釋放(解鎖),其它線程得到通知取得該book對(duì)象繼續(xù)執(zhí)行。
很多情況下,可以使用synchronized (this){...}來同步代碼塊。但需要注意的是,使用this作為同步對(duì)象的話,如果同一個(gè)類中存在多個(gè)synchronized (this){...}代碼塊,其中任何一個(gè)synchronized(this)代碼塊處于被執(zhí)行狀態(tài),則其它線程對(duì)其他synchronized(this)代碼塊的訪問也會(huì)受到阻塞。
為了說明這個(gè)問題,我們舉例說明:

- publicclass HelloSynchronized {
- publicstaticvoid main(String[] args) {
- //
- HelloSynchronized helloSynchronized = new HelloSynchronized();
- //創(chuàng)建2個(gè)線程t1, t2,分別調(diào)用HelloSynchronized helloSynchronized的2個(gè)方法method1,與method2
- Thread t1 = new Thread(new HelloSynchronizedRunnalbe(helloSynchronized, "method1"), "t1");
- Thread t2 = new Thread(new HelloSynchronizedRunnalbe(helloSynchronized, "method2"), "t2");
- t1.start();
- t2.start();
- }
- //synchronized public void method1() { //同步方法
- publicvoid method1() {
- synchronized (this) { //同步塊
- System.out.println(Thread.currentThread().getName()
- + " enter method1");
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- // do nothing
- }
- System.out.println(Thread.currentThread().getName()
- + " exit method1");
- }
- }
- //synchronized public void method2() { //同步方法
- publicvoid method2() {
- synchronized (this) { //同步塊
- System.out.println(Thread.currentThread().getName()
- + " enter method2");
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- // do nothing
- }
- System.out.println(Thread.currentThread().getName()
- + " exit method2");
- }
- }
- }
- class HelloSynchronizedRunnalbe implements Runnable {
- private HelloSynchronized helloSynchronized;
- private String methodName;
- public HelloSynchronizedRunnalbe(HelloSynchronized helloSynchronized, String methodName) {
- this.helloSynchronized = helloSynchronized;
- this.methodName = methodName;
- }
- publicvoid run() {
- if (methodName.equals("method1")) {
- helloSynchronized.method1();
- } elseif (methodName.equals("method2")) {
- helloSynchronized.method2();
- }
- }
- }
運(yùn)行結(jié)果為:
t1 exit method1
t2 enter method2
t2 exit method2
再把synchronized (this)去掉,運(yùn)行結(jié)果為:
t2 enter method2
t1 exit method1
t2 exit method2
同步方法例:
- synchronizedprivatevoid sellBook(Book book) {
- ...
- }
這種方法其實(shí)相當(dāng)于
- privatevoid sellBook(Book book) {
- synchronized(this) {
- ...
- }
- }
由于默認(rèn)采用this作為同步對(duì)象,所以當(dāng)一個(gè)類中有多個(gè)synchronized方法時(shí),同樣會(huì)存在以上問題:即如果有一個(gè)線程訪問其中某個(gè)synchronized方法時(shí),直到該方法執(zhí)行完畢,其它線程對(duì)其它synchronized方法的訪問也將受到阻塞。
大家可以把上面的例子稍加改造,去掉代碼中的synchronized (this),改為synchronized public void method1(),synchronized public void method2()同步形式,運(yùn)行后會(huì)得到同樣結(jié)果。
多同步代碼塊synchronized(this){...}的多線程阻塞問題(包括synchronized同步方法),在并發(fā)處理的系統(tǒng)中(比如WEB服務(wù)器)會(huì)嚴(yán)重影響性能,建議慎重使用。可以使用synchronized(obj){...}縮小同步資源對(duì)象的范圍來解決這個(gè)問題。
posted @ 2009-12-14 19:33 異域流浪 閱讀(288) | 評(píng)論 (0) | 編輯 收藏