http://www.tuicool.com/articles/2aeUBfe
楔子
最近一直都比較忙,沒(méi)有時(shí)間寫(xiě)博客了。今天項(xiàng)目終于灰度了,可以有時(shí)間寫(xiě)寫(xiě)博客,看看文章了!!!╮(╯▽╰)╭
今天要寫(xiě)的主題是Java的基礎(chǔ)知識(shí),Synchronized和Lock鎖的區(qū)別!!!
區(qū)別
1、ReentrantLock擁有Synchronized相同的并發(fā)性和內(nèi)存語(yǔ)義,此外還多了 鎖投票,定時(shí)鎖等候和中斷鎖等候等特性。
線程A和B都要獲取對(duì)象O的鎖定,假設(shè)A獲取了對(duì)象O鎖,B將等待A釋放對(duì)O的鎖定
如果使用 synchronized ,如果A不釋放,B將一直等下去,不能被中斷
如果 使用ReentrantLock,如果A不釋放,可以使B在等待了足夠長(zhǎng)的時(shí)間以后,中斷等待,而干別的事情
ReentrantLock獲取鎖定與三種方式:
lock(), 如果獲取了鎖立即返回,如果別的線程持有鎖,當(dāng)前線程則一直處于休眠狀態(tài),直到獲取鎖
tryLock(), 如果獲取了鎖立即返回true,如果別的線程正持有鎖,立即返回false;
tryLock(long timeout,TimeUnit unit), 如果獲取了鎖定立即返回true,如果別的線程正持有鎖,會(huì)等待參數(shù)給定的時(shí)間,在等待的過(guò)程中,如果獲取了鎖定,就返回true,如果等待超時(shí),返回false;
lockInterruptibly:如果獲取了鎖定立即返回,如果沒(méi)有獲取鎖定,當(dāng)前線程處于休眠狀態(tài),直到或者鎖定,或者當(dāng)前線程被別的線程中斷
2、synchronized是在JVM層面上實(shí)現(xiàn)的,不但可以通過(guò)一些監(jiān)控工具監(jiān)控synchronized的鎖定,而且在代碼執(zhí)行時(shí)出現(xiàn)異常,JVM會(huì)自動(dòng)釋放鎖定,但是使用Lock則不行,lock是通過(guò)代碼實(shí)現(xiàn)的,要保證鎖定一定會(huì)被釋放,就必須將unLock()放到finally{}中
3、在資源競(jìng)爭(zhēng)不是很激烈的情況下,Synchronized的性能要優(yōu)于ReetrantLock,但是在資源競(jìng)爭(zhēng)很激烈的情況下,Synchronized的性能會(huì)下降幾十倍,但是ReetrantLock的性能能維持常態(tài);
5.0的多線程任務(wù)包對(duì)于同步的性能方面有了很大的改進(jìn),在原有synchronized關(guān)鍵字的基礎(chǔ)上,又增加了ReentrantLock,以及各種Atomic類。了解其性能的優(yōu)劣程度,有助與我們?cè)谔囟ǖ那樾蜗伦龀稣_的選擇。
簡(jiǎn)單的總結(jié)
synchronized:
在資源競(jìng)爭(zhēng)不是很激烈的情況下,偶爾會(huì)有同步的情形下,synchronized是很合適的。原因在于,編譯程序通常會(huì)盡可能的進(jìn)行優(yōu)化synchronize,另外可讀性非常好,不管用沒(méi)用過(guò)5.0多線程包的程序員都能理解。
ReentrantLock:
ReentrantLock提供了多樣化的同步,比如有時(shí)間限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在資源競(jìng)爭(zhēng)不激烈的情形下,性能稍微比synchronized差點(diǎn)點(diǎn)。但是當(dāng)同步非常激烈的時(shí)候,synchronized的性能一下子能下降好幾十倍。而ReentrantLock確還能維持常態(tài)。
Atomic:
和上面的類似,不激烈情況下,性能比synchronized略遜,而激烈的時(shí)候,也能維持常態(tài)。激烈的時(shí)候,Atomic的性能會(huì)優(yōu)于ReentrantLock一倍左右。但是其有一個(gè)缺點(diǎn),就是只能同步一個(gè)值,一段代碼中只能出現(xiàn)一個(gè)Atomic的變量,多于一個(gè)同步無(wú)效。因?yàn)樗荒茉诙鄠€(gè)Atomic之間同步。
所以,我們寫(xiě)同步的時(shí)候,優(yōu)先考慮synchronized,如果有特殊需要,再進(jìn)一步優(yōu)化。ReentrantLock和Atomic如果用的不好,不僅不能提高性能,還可能帶來(lái)災(zāi)難。
測(cè)試結(jié)果
先貼測(cè)試結(jié)果:再貼代碼(Atomic測(cè)試代碼不準(zhǔn)確,一個(gè)同步中只能有1個(gè)Actomic,這里用了2個(gè),但是這里的測(cè)試只看速度)
round:100000 thread:5
Sync = 35301694
Lock = 56255753
Atom = 43467535
round:200000 thread:10
Sync = 110514604
Lock = 204235455
Atom = 170535361
round:300000 thread:15
Sync = 253123791
Lock = 448577123
Atom = 362797227
round:400000 thread:20
Sync = 16562148262
Lock = 846454786
Atom = 667947183
round:500000 thread:25
Sync = 26932301731
Lock = 1273354016
Atom = 982564544
Java代碼
package test.thread; import static java.lang.System.out; import java.util.Random; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantLock; public class TestSyncMethods { public static void test(int round,int threadNum,CyclicBarrier cyclicBarrier){ new SyncTest("Sync",round,threadNum,cyclicBarrier).testTime(); new LockTest("Lock",round,threadNum,cyclicBarrier).testTime(); new AtomicTest("Atom",round,threadNum,cyclicBarrier).testTime(); } public static void main(String args[]){ for(int i=0;i<5;i++){ int round=100000*(i+1); int threadNum=5*(i+1); CyclicBarrier cb=new CyclicBarrier(threadNum*2+1); out.println("=========================="); out.println("round:"+round+" thread:"+threadNum); test(round,threadNum,cb); } } } class SyncTest extends TestTemplate{ public SyncTest(String _id,int _round,int _threadNum,CyclicBarrier _cb){ super( _id, _round, _threadNum, _cb); } @Override /** * synchronized關(guān)鍵字不在方法簽名里面,所以不涉及重載問(wèn)題 */ synchronized long getValue() { return super.countValue; } @Override synchronized void sumValue() { super.countValue+=preInit[index++%round]; } } class LockTest extends TestTemplate{ ReentrantLock lock=new ReentrantLock(); public LockTest(String _id,int _round,int _threadNum,CyclicBarrier _cb){ super( _id, _round, _threadNum, _cb); } /** * synchronized關(guān)鍵字不在方法簽名里面,所以不涉及重載問(wèn)題 */ @Override long getValue() { try{ lock.lock(); return super.countValue; }finally{ lock.unlock(); } } @Override void sumValue() { try{ lock.lock(); super.countValue+=preInit[index++%round]; }finally{ lock.unlock(); } } } class AtomicTest extends TestTemplate{ public AtomicTest(String _id,int _round,int _threadNum,CyclicBarrier _cb){ super( _id, _round, _threadNum, _cb); } @Override /** * synchronized關(guān)鍵字不在方法簽名里面,所以不涉及重載問(wèn)題 */ long getValue() { return super.countValueAtmoic.get(); } @Override void sumValue() { super.countValueAtmoic.addAndGet(super.preInit[indexAtomic.get()%round]); } } abstract class TestTemplate{ private String id; protected int round; private int threadNum; protected long countValue; protected AtomicLong countValueAtmoic=new AtomicLong(0); protected int[] preInit; protected int index; protected AtomicInteger indexAtomic=new AtomicInteger(0); Random r=new Random(47); //任務(wù)柵欄,同批任務(wù),先到達(dá)wait的任務(wù)掛起,一直等到全部任務(wù)到達(dá)制定的wait地點(diǎn)后,才能全部喚醒,繼續(xù)執(zhí)行 private CyclicBarrier cb; public TestTemplate(String _id,int _round,int _threadNum,CyclicBarrier _cb){ this.id=_id; this.round=_round; this.threadNum=_threadNum; cb=_cb; preInit=new int[round]; for(int i=0;i<preInit.length;i++){ preInit[i]=r.nextInt(100); } } abstract void sumValue(); /* * 對(duì)long的操作是非原子的,原子操作只針對(duì)32位 * long是64位,底層操作的時(shí)候分2個(gè)32位讀寫(xiě),因此不是線程安全 */ abstract long getValue(); public void testTime(){ ExecutorService se=Executors.newCachedThreadPool(); long start=System.nanoTime(); //同時(shí)開(kāi)啟2*ThreadNum個(gè)數(shù)的讀寫(xiě)線程 for(int i=0;i<threadNum;i++){ se.execute(new Runnable(){ public void run() { for(int i=0;i<round;i++){ sumValue(); } //每個(gè)線程執(zhí)行完同步方法后就等待 try { cb.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BrokenBarrierException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); se.execute(new Runnable(){ public void run() { getValue(); try { //每個(gè)線程執(zhí)行完同步方法后就等待 cb.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BrokenBarrierException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); } try { //當(dāng)前統(tǒng)計(jì)線程也wait,所以CyclicBarrier的初始值是threadNum*2+1 cb.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BrokenBarrierException e) { // TODO Auto-generated catch block e.printStackTrace(); } //所有線程執(zhí)行完成之后,才會(huì)跑到這一步 long duration=System.nanoTime()-start; out.println(id+" = "+duration); } }
補(bǔ)充知識(shí)
CyclicBarrier和CountDownLatch一樣,都是關(guān)于線程的計(jì)數(shù)器。
- CyclicBarrier初始化時(shí)規(guī)定一個(gè)數(shù)目,然后計(jì)算調(diào)用了CyclicBarrier.await()進(jìn)入等待的線程數(shù)。當(dāng)線程數(shù)達(dá)到了這個(gè)數(shù)目時(shí),所有進(jìn)入等待狀態(tài)的線程被喚醒并繼續(xù)。
- CyclicBarrier就象它名字的意思一樣,可看成是個(gè)障礙, 所有的線程必須到齊后才能一起通過(guò)這個(gè)障礙。
- CyclicBarrier初始時(shí)還可帶一個(gè)Runnable的參數(shù), 此Runnable任務(wù)在CyclicBarrier的數(shù)目達(dá)到后,所有其它線程被喚醒前被執(zhí)行。