一、共享資源競(jìng)爭(zhēng)問(wèn)題

      在Java語(yǔ)言的并發(fā)編程中,由于我們不知道線程實(shí)際上在何時(shí)運(yùn)行,所以在實(shí)際多線程編程中,如果兩個(gè)線程訪問(wèn)相同的資源,那么由于線程運(yùn)行的不確定性便會(huì)在這種多線程中產(chǎn)生訪問(wèn)錯(cuò)誤。所以為了避免這一情況的發(fā)生,我們?cè)诰幊痰臅r(shí)候需要把并發(fā)執(zhí)行的線程中用于訪問(wèn)這一共享資源的方法進(jìn)行同步處理,以避免并發(fā)對(duì)于共享資源產(chǎn)生的影響。

      并發(fā)模式在解決線程沖突的問(wèn)題時(shí),基本上都是采用序列化訪問(wèn)共享資源的方案。這在我的理解中,就是我們要控制同一時(shí)刻只能讓一個(gè)線程對(duì)這一共享資源進(jìn)行訪問(wèn)。

二、synchronized關(guān)鍵字的使用

1.synchronized對(duì)于類(lèi)普通成員方法的修飾

      Java語(yǔ)言中,每一個(gè)對(duì)象都含有單一的鎖(監(jiān)視器)。而synchronized的作用之一就是修飾使用了共享資源的成員方法,這樣在線程通過(guò)對(duì)象調(diào)用該方法時(shí),該對(duì)象都會(huì)被加鎖。這時(shí)候如果需要調(diào)用該對(duì)象的另一個(gè)synchronized方法,則需要在第一個(gè)方法調(diào)用完畢后再進(jìn)行,這就實(shí)現(xiàn)了最基本的同步。

      例1:使用synchronized修飾方法和未修飾方法的區(qū)別

    (1)使用synchronized修飾過(guò)的方法,在多線程執(zhí)行的過(guò)程中,程序依次輸出遞增3的數(shù)字

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開(kāi)發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

 1 import java.util.concurrent.ExecutorService; 2 import java.util.concurrent.Executors; 3  4 public class Synchronization implements Runnable { 5     private static int currentCount = 0; 6     synchronized void printAdd() { 7         currentCount++; 8         Thread.yield(); 9         currentCount++;10         Thread.yield();11         currentCount++;12         System.out.println(currentCount);13     }14     @Override15     public void run() {16         printAdd();17     }18     public static void main(String[] args) {19         ExecutorService exec = Executors.newCachedThreadPool();20         Synchronization test = new Synchronization();21         for(int i = 0; i < 100; i++) {22             exec.execute(test);23         }24         exec.shutdown();25     }26 }

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開(kāi)發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

 

    (2)與之相對(duì)應(yīng)的未用synchronized修飾過(guò)的方法,在多線程執(zhí)行的過(guò)程中,程序會(huì)輸出沒(méi)有規(guī)律的數(shù)字

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開(kāi)發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

 1 import java.util.concurrent.ExecutorService; 2 import java.util.concurrent.Executors; 3  4 public class Synchronization implements Runnable { 5     private static int currentCount = 0; 6     void printAdd() { 7         currentCount++; 8         Thread.yield(); 9         currentCount++;10         Thread.yield();11         currentCount++;12         System.out.println(currentCount);13     }14     @Override15     public void run() {16         printAdd();17     }18     public static void main(String[] args) {19         ExecutorService exec = Executors.newCachedThreadPool();20         Synchronization test = new Synchronization();21         for(int i = 0; i < 100; i++) {22             exec.execute(test);23         }24         exec.shutdown();25     }26 }

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開(kāi)發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

 

2.synchronized對(duì)于類(lèi)靜態(tài)成員方法的修飾

      與對(duì)象相同,Java的每個(gè)類(lèi)也有一個(gè)鎖,所以我們可以通過(guò)將靜態(tài)方法用synchronized修飾來(lái)控制其對(duì)于靜態(tài)共享資源的訪問(wèn)。

三、Lock的使用

      在上面的利用synchronized進(jìn)行同步的描述中,我們都是利用方法所在對(duì)象自身的鎖來(lái)進(jìn)行同步。除了這種方法之外,我們還可以用Java語(yǔ)言中內(nèi)置的鎖對(duì)象來(lái)進(jìn)行顯式的加鎖。

      Lock接口,便是Java語(yǔ)言在java.util.concurrent.locks包中為我們提供的顯式鎖。目前在該包中有三個(gè)Lock的實(shí)現(xiàn)(基于JDK 1.7),分別為ReentrantLock,ReentrantReadWriteLock.ReadLock,ReentrantReadWriteLock.WriteLock。

      Lock對(duì)象必須在程序中被顯式的創(chuàng)建、鎖定和釋放。

      例3:使用Lock實(shí)現(xiàn)多線程之間的同步

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開(kāi)發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

 1 import java.util.concurrent.ExecutorService; 2 import java.util.concurrent.Executors; 3 import java.util.concurrent.locks.Lock; 4 import java.util.concurrent.locks.ReentrantLock; 5  6 public class LockTest implements Runnable { 7      8     private static int currentCount = 0; 9     Lock lock = new ReentrantLock();10     void addCount() {11         lock.lock();12         try {13             currentCount++;14             Thread.yield();15             currentCount++;16             Thread.yield();17             currentCount++;18             System.out.println(currentCount);19         } finally {20             lock.unlock();21         }22     }23     @Override24     public void run() {25         addCount();26     }27     public static void main(String[] args) {28         ExecutorService exec = Executors.newCachedThreadPool();29         LockTest test = new LockTest();30         for(int i = 0; i < 100; i++) {31             exec.execute(test);32         }33         exec.shutdown();34     }35 }

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開(kāi)發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

 

四、synchronized與Lock的對(duì)比

      在我的理解中,synchronized修飾的方法,在檢查到對(duì)象已經(jīng)被加鎖的情況后,會(huì)等待到該對(duì)象鎖被釋放;之后對(duì)對(duì)象進(jìn)行加鎖,進(jìn)行自身方法的執(zhí)行。

      但是Lock則不是如此,Lock可以嘗試獲取鎖一段時(shí)間,或者嘗試獲取鎖最后失敗,而synchronized方式則不可以。綜合來(lái)說(shuō),采用Lock顯式鎖可以完成更多并發(fā)控制功能,但是其較synchronized麻煩許多,所以根據(jù)自身程序的需要可以視情況選擇這兩種同步方法。

五、總結(jié)

      本篇文章是簡(jiǎn)單的介紹了synchronized 及Lock的使用,Lock的高級(jí)使用將在下一篇文章進(jìn)行介紹。小弟才疏學(xué)淺,如有錯(cuò)誤,請(qǐng)多指出。