并發(fā)編程第二篇
不正確的訪問資源
在Java中榛瓮,遞增不是原子操作
-
當(dāng)多線程操作EvenGerator對象時(shí)潘悼,A線程正在操作第一個(gè)++currentEvenValue,此刻又進(jìn)來一個(gè)線程B操作拧烦,就產(chǎn)生出現(xiàn)了并發(fā)問題。
public class EvenGenerator extends IntGenerator { private int currentEvenValue = 0; public int next() { ++currentEvenValue; // Danger point here! ++currentEvenValue; return currentEvenValue; } public static void main(String[] args) { EvenChecker.test(new EvenGenerator(),10); } } public class EvenChecker implements Runnable { private IntGenerator generator; private final int id; public EvenChecker(IntGenerator g, int ident) { generator = g; id = ident; } public void run() { while (!generator.isCanceled()) { int val = generator.next(); if (val % 2 != 0) { System.out.println(val + " not even!"); generator.cancel(); // Cancels all EvenCheckers } } } // Test any type of IntGenerator: public static void test(IntGenerator gp, int count) { ExecutorService exec = Executors.newCachedThreadPool(); for (int i = 0; i < count; i++) exec.execute(new EvenChecker(gp, i)); exec.shutdown(); } }
解決共享資源競爭
Java提供關(guān)鍵字synchronized鎖形式挂绰,當(dāng)任務(wù)要執(zhí)行被synchronized關(guān)鍵字保護(hù)的代碼時(shí)屎篱,需要先請求鎖(不可用需等待其他任務(wù)釋放鎖),執(zhí)行代碼葵蒂,釋放鎖交播。
JVM負(fù)責(zé)跟蹤對象被加鎖的次數(shù),通俗的講當(dāng)任務(wù)獲取到當(dāng)前對象的鎖時(shí)(對于其他任務(wù)來講就是給此對象加鎖)践付,此對象的加鎖次數(shù)+1秦士,再操作此對象其他加鎖的方法時(shí),再+1(只有首先獲得對象的鎖才可以持續(xù)獲取多個(gè)鎖)永高,最后當(dāng)任務(wù)離開加鎖的方法時(shí)(釋放鎖)隧土,加鎖次數(shù)遞減提针,直至為0;對于其他任務(wù)來講就可以獲取鎖了曹傀,依次循環(huán)辐脖。
-
鎖分類:方法鎖(其實(shí)也是對象鎖),對象鎖皆愉,類鎖(靜態(tài)方法鎖)嗜价。
同步使用場景:
如果你正在寫一個(gè)變量,它接下來可能會(huì)被其他線程操作幕庐,獲取你正在操作的變量剛被其他線程操作過久锥,就應(yīng)該使用同步。并且异剥,讀寫線程都需要使用相同的監(jiān)視器(鎖)瑟由。
注意事項(xiàng):
1.使用并發(fā)時(shí),將域設(shè)置為private時(shí)非常重要的冤寿,否則synchronized關(guān)鍵字就不能防止其他任務(wù)訪問歹苦,將會(huì)產(chǎn)生沖突。
public class SynchronizedEvenGenerator extends IntGenerator { private int currentEvenValue = 0; public synchronized int next() { ++currentEvenValue; // Thread.yield(); // Cause failure faster // ++currentEvenValue; return currentEvenValue; } public static void main(String[] args) { EvenChecker.test(new SynchronizedEvenGenerator()); } }
-
使用顯示的Lock對象:Lock對象必須顯示的創(chuàng)建疚沐、鎖定和釋放暂氯,雖然看起來不太優(yōu)雅,但會(huì)更靈活一些亮蛔。
public class MutexEvenGenerator extends IntGenerator { private int currentEvenValue = 0; private Lock lock = new ReentrantLock(); public int next() { lock.lock(); try { ++currentEvenValue; Thread.yield(); // Cause failure faster ++currentEvenValue; return currentEvenValue; } finally { lock.unlock(); } } public static void main(String[] args) { EvenChecker.test(new MutexEvenGenerator()); } }
- ReentrantLock重入鎖:允許你嘗試獲取但最終未獲取鎖痴施,即使別人獲取了鎖你也可以去執(zhí)行其他事情,而不用一直等待究流。