Lock与synchronized 的区别
1、ReentrantLock 擁有Synchronized相同的并發(fā)性和內(nèi)存語義,此外還多了 鎖投票,定時(shí)鎖等候和中斷鎖等候
線程A和B都要獲取對(duì)象O的鎖定,假設(shè)A獲取了對(duì)象O鎖,B將等待A釋放對(duì)O的鎖定,
如果使用 synchronized ,如果A不釋放,B將一直等下去,不能被中斷
如果 使用ReentrantLock,如果A不釋放,可以使B在等待了足夠長(zhǎng)的時(shí)間以后,中斷等待,而干別的事情
ReentrantLock獲取鎖定與三種方式:
a) lock(), 如果獲取了鎖立即返回,如果別的線程持有鎖,當(dāng)前線程則一直處于休眠狀態(tài),直到獲取鎖
b) tryLock(), 如果獲取了鎖立即返回true,如果別的線程正持有鎖,立即返回false;
c)tryLock(long timeout,TimeUnit unit), 如果獲取了鎖定立即返回true,如果別的線程正持有鎖,會(huì)等待參數(shù)給定的時(shí)間,在等待的過程中,如果獲取了鎖定,就返回true,如果等待超時(shí),返回false;
d) lockInterruptibly:如果獲取了鎖定立即返回,如果沒有獲取鎖定,當(dāng)前線程處于休眠狀態(tài),直到或者鎖定,或者當(dāng)前線程被別的線程中斷
2、synchronized是在JVM層面上實(shí)現(xiàn)的,不但可以通過一些監(jiān)控工具監(jiān)控synchronized的鎖定,而且在代碼執(zhí)行時(shí)出現(xiàn)異常,JVM會(huì)自動(dòng)釋放鎖定,但是使用Lock則不行,lock是通過代碼實(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é)論先擺出來:
synchronized:
在資源競(jìng)爭(zhēng)不是很激烈的情況下,偶爾會(huì)有同步的情形下,synchronized是很合適的。原因在于,編譯程序通常會(huì)盡可能的進(jìn)行優(yōu)化synchronize,另外可讀性非常好,不管用沒用過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è)同步無效。因?yàn)樗荒茉诙鄠€(gè)Atomic之間同步。
所以,我們寫同步的時(shí)候,優(yōu)先考慮synchronized,如果有特殊需要,再進(jìn)一步優(yōu)化。ReentrantLock和Atomic如果用的不好,不僅不能提高性能,還可能帶來災(zāi)難。
先貼測(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
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)鍵字不在方法簽名里面,所以不涉及重載問題
*/
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)鍵字不在方法簽名里面,所以不涉及重載問題
*/
@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)鍵字不在方法簽名里面,所以不涉及重載問題
*/
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);
}
}
}
一、synchronize修飾不同代碼都是鎖住了什么?
大家都知道synchronize可以修飾屬性、代碼塊,方法、類,但是修飾不同的代碼鎖住的內(nèi)容是不同的。
1、修飾非靜態(tài)屬性和方法時(shí),拿到的是調(diào)用這個(gè)方法或者屬性的對(duì)象(this)的鎖。
2、synchronize()修飾代碼塊時(shí),拿到的是指定對(duì)象的鎖。
3、修飾類、靜態(tài)方法、靜態(tài)代碼塊時(shí),由于沒有this指針,因此拿到的是類鎖,也就是該類的class對(duì)象。
!!!注意:一個(gè)對(duì)象只有一個(gè)鎖
二、關(guān)于synchronize
由于synchronize是由JVM實(shí)現(xiàn)的,因此當(dāng)加鎖代碼出現(xiàn)異常時(shí),對(duì)象鎖可以由JVM釋放,包含以下三種情況:
1、 占有鎖的線程執(zhí)行完了代碼塊,然后釋放對(duì)鎖的占有;
2、 占有鎖的線程發(fā)生了異常,此時(shí)JVM會(huì)讓線程自動(dòng)釋放鎖;
3、 占有鎖的線程調(diào)用了wait()方法,從而進(jìn)入了WAITING狀態(tài)需要釋放鎖。
三、關(guān)于Lock
由于Lock是由JDK實(shí)現(xiàn)的,所以不像synchronize鎖的獲取和釋放都是由JVM控制的,Lock的獲取和釋放都需要手動(dòng)進(jìn)行,并且在發(fā)生異常時(shí),不會(huì)自動(dòng)釋放鎖。因此一般來說,使用Lock必須在try{}catch{}塊中進(jìn)行,并且將釋放鎖的操作放在finally塊中進(jìn)行,以保證鎖一定被被釋放,防止死鎖的發(fā)生。
1、lock() 獲取鎖與釋放鎖 ,如果鎖已被其他線程獲取,則進(jìn)行等待。
復(fù)制代碼
1 Lock lock = …; lock.lock();
2 try{
3 //處理任務(wù)
4 }catch(Exception ex){
5 }finally{
6 lock.unlock(); //釋放鎖
7 }
復(fù)制代碼
2、tryLock() 獲取鎖時(shí)有返回值可以得知獲取鎖操作是否成功
復(fù)制代碼
1 Lock lock = …;
2 if(lock.tryLock()) {
3 try{
4 //處理任務(wù)
5 }catch(Exception ex){
6 }finally{
7 lock.unlock(); //釋放鎖
8 }
9 }else {
10 //如果不能獲取鎖,則直接做其他事情
11 }
復(fù)制代碼
如果獲取成功則返回True,如果鎖被其他線程占用則返回FALSE,該方法會(huì)立即返回結(jié)果,不會(huì)讓線程一直處于等待狀態(tài)。
3、tryLock(long time,TimeUnit unit) 與tryLock() 類似,但是與tryLock立即返回結(jié)果不同,該方法在拿不到鎖的情況下回等待time時(shí)間,如果在限定時(shí)間內(nèi)還是拿不到鎖就返回FALSE,如果在一開始或者等待時(shí)間內(nèi)拿到鎖則返回TRUE。
4、lockInterruptibly()
通過這個(gè)方法嘗試獲取鎖時(shí),如果線程正在等待獲取鎖,則該線程可以響應(yīng)Thread.interrupt()中斷。synchronize對(duì)于沒有獲得鎖處于等待狀態(tài)的線程無法響應(yīng)中斷。
1 public void method() throws InterruptedException {
2 lock.lockInterruptibly();
3 try { //… }
4 finally { lock.unlock(); }
5 }
lockInterruptibly方法必須放在try塊中或者在調(diào)用lockInterruptibly的方法外聲明拋出InterruptedException,推薦使用后者。
5、readWriteLock()
該鎖提升了讀操作的效率,不過要注意的是,如果有一個(gè)線程已經(jīng)占用了讀鎖,則此時(shí)其他線程如果要申請(qǐng)寫鎖,則申請(qǐng)寫鎖的線程會(huì)一直等待釋放讀鎖。如果有一個(gè)線程已經(jīng)占用了寫鎖,則此時(shí)其他線程如果申請(qǐng)寫鎖或者讀鎖,則申請(qǐng)的線程也會(huì)一直等待釋放寫鎖。
四、Lock和synchronized的選擇:
1、 Lock是一個(gè)接口,屬于JDK層面的實(shí)現(xiàn);而synchronized屬于Java語言的特性,其實(shí)現(xiàn)有JVM來控制(代碼執(zhí)行完畢,出現(xiàn)異常,wait時(shí)JVM會(huì)主動(dòng)釋放鎖)。
2、 synchronized在發(fā)生異常時(shí),會(huì)自動(dòng)釋放掉鎖,故不會(huì)發(fā)生死鎖現(xiàn)(此時(shí)的死鎖一般是代碼邏輯引起的);而Lock必須在finally中主動(dòng)unlock鎖,否則就會(huì)出現(xiàn)死鎖。
3、 Lock能夠響應(yīng)中斷,讓等待狀態(tài)的線程停止等待;而synchronized不行。
4、 通過Lock可以知道線程是否成功獲得了鎖,而synchronized不行。
5、 Lock提高了多線程下對(duì)讀操作的效率。
五、擴(kuò)展
1、可重入鎖:synchronized和ReentrantLock都是可重入鎖。當(dāng)一個(gè)線程執(zhí)行到某個(gè)synchronized方法時(shí),比如說method1,而在method1中會(huì)調(diào)用另外一個(gè)synchronized方法method2,此時(shí)線程不必重新去申請(qǐng)鎖,而是可以直接執(zhí)行方法method2。
2、可中斷鎖:等待獲得鎖的等待過程是否可以中斷。通過上面的例子,我們可以得知Lock是可中斷鎖,而synchronized不是。
3、公平鎖:盡量以請(qǐng)求的順序來獲取鎖,同是有多個(gè)線程在等待一個(gè)鎖,當(dāng)這個(gè)鎖被釋放時(shí),等待時(shí)間最久的線程(最先請(qǐng)求的線程)會(huì)獲得該鎖。synchronized是非公平鎖,而ReentrantLock和ReentReadWriteLock默認(rèn)情況下是非公平鎖,但是可以設(shè)置成公平鎖。
ReentrantLock lock = new ReentrantLock(true);
ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
設(shè)置為TRUE即為公平鎖,為FALSE或者無參數(shù)為非公平鎖。
4、讀寫鎖:讀寫鎖將對(duì)臨界資源的訪問分成了兩個(gè)鎖,一個(gè)讀鎖和一個(gè)寫鎖。增加讀寫靈活性。即ReadWriteLock接口及其實(shí)現(xiàn)ReentrantReadWriteLock。
總結(jié)
以上是生活随笔為你收集整理的Lock与synchronized 的区别的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: springboot打包不同环境配置与s
- 下一篇: 深入分析Synchronized原理(阿