一、共享資源競爭問題
在Java語言的并發(fā)編程中,由于我們不知道線程實(shí)際上在何時(shí)運(yùn)行,所以在實(shí)際多線程編程中,如果兩個線程訪問相同的資源,那么由于線程運(yùn)行的不確定性便會在這種多線程中產(chǎn)生訪問錯誤。所以為了避免這一情況的發(fā)生,我們在編程的時(shí)候需要把并發(fā)執(zhí)行的線程中用于訪問這一共享資源的方法進(jìn)行同步處理,以避免并發(fā)對于共享資源產(chǎn)生的影響。
并發(fā)模式在解決線程沖突的問題時(shí),基本上都是采用序列化訪問共享資源的方案。這在我的理解中,就是我們要控制同一時(shí)刻只能讓一個線程對這一共享資源進(jìn)行訪問。
二、synchronized關(guān)鍵字的使用
1.synchronized對于類普通成員方法的修飾
Java語言中,每一個對象都含有單一的鎖(監(jiān)視器)。而synchronized的作用之一就是修飾使用了共享資源的成員方法,這樣在線程通過對象調(diào)用該方法時(shí),該對象都會被加鎖。這時(shí)候如果需要調(diào)用該對象的另一個synchronized方法,則需要在第一個方法調(diào)用完畢后再進(jìn)行,這就實(shí)現(xiàn)了最基本的同步。
例1:使用synchronized修飾方法和未修飾方法的區(qū)別
(1)使用synchronized修飾過的方法,在多線程執(zhí)行的過程中,程序依次輸出遞增3的數(shù)字
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 }
(2)與之相對應(yīng)的未用synchronized修飾過的方法,在多線程執(zhí)行的過程中,程序會輸出沒有規(guī)律的數(shù)字
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 }
2.synchronized對于類靜態(tài)成員方法的修飾
與對象相同,Java的每個類也有一個鎖,所以我們可以通過將靜態(tài)方法用synchronized修飾來控制其對于靜態(tài)共享資源的訪問。
三、Lock的使用
在上面的利用synchronized進(jìn)行同步的描述中,我們都是利用方法所在對象自身的鎖來進(jìn)行同步。除了這種方法之外,我們還可以用Java語言中內(nèi)置的鎖對象來進(jìn)行顯式的加鎖。
Lock接口,便是Java語言在java.util.concurrent.locks包中為我們提供的顯式鎖。目前在該包中有三個Lock的實(shí)現(xiàn)(基于JDK 1.7),分別為ReentrantLock,ReentrantReadWriteLock.ReadLock,ReentrantReadWriteLock.WriteLock。
Lock對象必須在程序中被顯式的創(chuàng)建、鎖定和釋放。
例3:使用Lock實(shí)現(xià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 }
四、synchronized與Lock的對比
在我的理解中,synchronized修飾的方法,在檢查到對象已經(jīng)被加鎖的情況后,會等待到該對象鎖被釋放;之后對對象進(jìn)行加鎖,進(jìn)行自身方法的執(zhí)行。
但是Lock則不是如此,Lock可以嘗試獲取鎖一段時(shí)間,或者嘗試獲取鎖最后失敗,而synchronized方式則不可以。綜合來說,采用Lock顯式鎖可以完成更多并發(fā)控制功能,但是其較synchronized麻煩許多,所以根據(jù)自身程序的需要可以視情況選擇這兩種同步方法。
五、總結(jié)
本篇文章是簡單的介紹了synchronized 及Lock的使用,Lock的高級使用將在下一篇文章進(jìn)行介紹。小弟才疏學(xué)淺,如有錯誤,請多指出。
http://www.cnblogs.com/JaydenRansom/p/6480990.html