最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java synchronized與CAS使用方式詳解

 更新時(shí)間:2023年01月16日 15:25:20   作者:Jinmindong  
提到Java的知識(shí)點(diǎn)一定會(huì)有多線程,JDK版本不斷的更迭很多新的概念和方法也都響應(yīng)提出,但是多線程和線程安全一直是一個(gè)重要的關(guān)注點(diǎn)。比如說(shuō)我們一入門(mén)就學(xué)習(xí)的synchronized怎么個(gè)實(shí)現(xiàn)和原理,還有總是被提到的CAS是啥,他和synchronized關(guān)系是啥?請(qǐng)往下看

引言

上一篇文章中我們說(shuō)過(guò),volatile通過(guò)lock指令保證了可見(jiàn)性、有序性以及“部分”原子性。但在大部分并發(fā)問(wèn)題中,都需要保證操作的原子性,volatile并不具有該功能,這時(shí)就需要通過(guò)其他手段來(lái)達(dá)到線程安全的目的,在Java編程中,我們可以通過(guò)鎖、synchronized關(guān)鍵字,以及CAS操作來(lái)達(dá)到線程安全的目的。

synchronized

在Java的并發(fā)編程中,保證線程同步最為程序員所熟悉的就是synchronized關(guān)鍵字,synchronized關(guān)鍵字最為方便的地方是他不需要顯示的管理鎖的釋放,極大減少了編程出錯(cuò)的概率。

在Java1.5及以前的版本中,synchronized并不是同步最好的選擇,由于并發(fā)時(shí)頻繁的阻塞和喚醒線程,會(huì)浪費(fèi)許多資源在線程狀態(tài)的切換上,導(dǎo)致了synchronized的并發(fā)效率在某些情況下不如ReentrantLock。在Java1.6的版本中,對(duì)synchronized進(jìn)行了許多優(yōu)化,極大的提高了synchronized的性能。只要synchronized能滿足使用環(huán)境,建議使用synchronized而不使用ReentrantLock。

synchronized的三種使用方式

1.修飾實(shí)例方法,為當(dāng)前實(shí)例加鎖,進(jìn)入同步方法前要獲得當(dāng)前實(shí)例的鎖。

2.修飾靜態(tài)方法,為當(dāng)前類對(duì)象加鎖,進(jìn)入同步方法前要獲得當(dāng)前類對(duì)象的鎖。

3.修飾代碼塊,指定加鎖對(duì)象,對(duì)給定對(duì)象加鎖,進(jìn)入同步代碼塊前要獲得給定對(duì)象的鎖。

這三種使用方式大家應(yīng)該都很熟悉,有一個(gè)要注意的地方是對(duì)靜態(tài)方法的修飾可以和實(shí)例方法的修飾同時(shí)使用,不會(huì)阻塞,因?yàn)橐粋€(gè)是修飾的Class類,一個(gè)是修飾的實(shí)例對(duì)象。下面的例子可以說(shuō)明這一點(diǎn):

public class SynchronizedTest {
	public static synchronized void StaticSyncTest() {
		for (int i = 0; i < 3; i++) {
			System.out.println("StaticSyncTest");
			try {
				TimeUnit.SECONDS.sleep(1);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	public synchronized void NonStaticSyncTest() {
		for (int i = 0; i < 3; i++) {
			System.out.println("NonStaticSyncTest");
			try {
				TimeUnit.SECONDS.sleep(1);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
public static void main(String[] args) throws InterruptedException {SynchronizedTest synchronizedTest = new SynchronizedTest();new Thread(new Runnable() {
		@Override
		public void run() {
			SynchronizedTest.StaticSyncTest();
		}
	}).start();new Thread(new Runnable() {
		@Override
		public void run() {
			synchronizedTest.NonStaticSyncTest();
		}
	}).start();
}
//StaticSyncTest
//NonStaticSyncTest
//StaticSyncTest
//NonStaticSyncTest
//StaticSyncTest
//NonStaticSyncTest 

代碼中我們開(kāi)啟了兩個(gè)線程分別鎖定靜態(tài)方法和實(shí)例方法,從打印的輸出結(jié)果中我們可以看到,這兩個(gè)線程鎖定的是不同對(duì)象,可以并發(fā)執(zhí)行。

synchronized的底層原理

我們看一段synchronized關(guān)鍵字經(jīng)過(guò)編譯后的字節(jié)碼:

if (null == instance) { 
	synchronized (DoubleCheck.class) {
		if (null == instance) { 
			instance = new DoubleCheck(); 
		}
	}
} 

可以看到synchronized關(guān)鍵字在同步代碼塊前后加入了monitorenter和monitorexit這兩個(gè)指令。monitorenter指令會(huì)獲取鎖對(duì)象,如果獲取到了鎖對(duì)象,就將鎖計(jì)數(shù)器加1,未獲取到則會(huì)阻塞當(dāng)前線程。monitorexit指令會(huì)釋放鎖對(duì)象,同時(shí)將鎖計(jì)數(shù)器減1。

JDK1.6對(duì)synchronized的優(yōu)化

JDK1.6對(duì)對(duì)synchronized的優(yōu)化主要體現(xiàn)在引入了“偏向鎖”和“輕量級(jí)鎖”的概念,同時(shí)synchronized的鎖只可升級(jí),不可降級(jí):

這里我不打算詳細(xì)講解每種鎖的實(shí)現(xiàn),想了解的可以參照《深入理解Java虛擬機(jī)》,只簡(jiǎn)單說(shuō)下自己的理解。

偏向鎖的思想是指如果一個(gè)線程獲得了鎖,那么就從無(wú)鎖模式進(jìn)入偏向模式,這一步是通過(guò)CAS操作來(lái)做的,進(jìn)入偏向模式的線程每一次訪問(wèn)這個(gè)鎖的同步代碼塊時(shí)都不需要再進(jìn)行同步操作,除非有其他線程訪問(wèn)這個(gè)鎖。

偏向鎖提高的是那些帶同步但無(wú)競(jìng)爭(zhēng)的代碼的性能,也就是說(shuō)如果你的同步代碼塊很長(zhǎng)時(shí)間都是同一個(gè)線程訪問(wèn),偏向鎖就會(huì)提高效率,因?yàn)樗麥p少了重復(fù)獲取鎖和釋放鎖產(chǎn)生的性能消耗。如果你的同步代碼塊會(huì)頻繁的在多個(gè)線程之間訪問(wèn),可以使用參數(shù)-XX:-UseBiasedLocking來(lái)禁止偏向鎖產(chǎn)生,避免在多個(gè)鎖狀態(tài)之間切換。

偏向鎖優(yōu)化了只有一個(gè)線程進(jìn)入同步代碼塊的情況,當(dāng)多個(gè)線程訪問(wèn)鎖時(shí)偏向鎖就升級(jí)為了輕量級(jí)鎖。

輕量級(jí)鎖的思想是當(dāng)多個(gè)線程進(jìn)入同步代碼塊后,多個(gè)線程未發(fā)生競(jìng)爭(zhēng)時(shí)一直保持輕量級(jí)鎖,通過(guò)CAS來(lái)獲取鎖。如果發(fā)生競(jìng)爭(zhēng),首先會(huì)采用CAS自旋操作來(lái)獲取鎖,自旋在極短時(shí)間內(nèi)發(fā)生,有固定的自旋次數(shù),一旦自旋獲取失敗,則升級(jí)為重量級(jí)鎖。

輕量級(jí)鎖優(yōu)化了多個(gè)線程進(jìn)入同步代碼塊的情況,多個(gè)線程未發(fā)生競(jìng)爭(zhēng)時(shí),可以通過(guò)CAS獲取鎖,減少鎖狀態(tài)切換。當(dāng)多個(gè)線程發(fā)生競(jìng)爭(zhēng)時(shí),不是直接阻塞線程,而是通過(guò)CAS自旋來(lái)嘗試獲取鎖,減少了阻塞線程的概率,這樣就提高了synchronized鎖的性能。

synchronized的等待喚醒機(jī)制

synchronized的等待喚醒是通過(guò)notify/notifyAll和wait三個(gè)方法來(lái)實(shí)現(xiàn)的,這三個(gè)方法的執(zhí)行都必須在同步代碼塊或同步方法中進(jìn)行,否則將會(huì)報(bào)錯(cuò)。

wait方法的作用是使當(dāng)前執(zhí)行代碼的線程進(jìn)行等待,notify/notifyAll相同,都是通知等待的代碼繼續(xù)執(zhí)行,notify只通知任一個(gè)正在等待的線程,notifyAll通知所有正在等待的線程。wait方法跟sleep不一樣,他會(huì)釋放當(dāng)前同步代碼塊的鎖,notify在通知任一等待的線程時(shí)不會(huì)釋放鎖,只有在當(dāng)前同步代碼塊執(zhí)行完成之后才會(huì)釋放鎖。下面的代碼可以說(shuō)明這一點(diǎn):

public static void main(String[] args) throws InterruptedException {waitThread();notifyThread();
}
private static Object lockObject = new Object();
private static void waitThread() {Thread watiThread = new Thread(new Runnable() {@Overridepublic void run() {synchronized (lockObject) {System.out.println(Thread.currentThread().getName() + "wait-before");try {TimeUnit.SECONDS.sleep(2);lockObject.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "after-wait");}}},"waitthread");watiThread.start();
}
private static void notifyThread() {Thread watiThread = new Thread(new Runnable() {@Overridepublic void run() {synchronized (lockObject) {System.out.println(Thread.currentThread().getName() + "notify-before");lockObject.notify();try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();} System.out.println(Thread.currentThread().getName() + "after-notify");}}},"notifythread");watiThread.start();
}
//waitthreadwait-before
//notifythreadnotify-before
//notifythreadafter-notify
//waitthreadafter-wait 

代碼中notify線程通知之后wait線程并沒(méi)有馬上啟動(dòng),還需要notity線程執(zhí)行完同步代碼塊釋放鎖之后wait線程才開(kāi)始執(zhí)行。

CAS

在synchronized的優(yōu)化過(guò)程中我們看到大量使用了CAS操作,CAS全稱Compare And Set(或Compare And Swap),CAS包含三個(gè)操作數(shù):內(nèi)存位置(V)、原值(A)、新值(B)。簡(jiǎn)單來(lái)說(shuō)CAS操作就是一個(gè)虛擬機(jī)實(shí)現(xiàn)的原子操作,這個(gè)原子操作的功能就是將舊值(A)替換為新值(B),如果舊值(A)未被改變,則替換成功,如果舊值(A)已經(jīng)被改變則替換失敗。

可以通過(guò)AtomicInteger類的自增代碼來(lái)說(shuō)明這個(gè)問(wèn)題,當(dāng)不使用同步時(shí)下面這段代碼很多時(shí)候不能得到預(yù)期值10000,因?yàn)閚oncasi[0]++不是原子操作。

private static void IntegerTest() throws InterruptedException {final Integer[] noncasi = new Integer[]{ 0 };for (int i = 0; i < 10; i++) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {for (int j = 0; j < 1000; j++) {noncasi[0]++;}}});thread.start();}while (Thread.activeCount() > 2) {Thread.sleep(10);}System.out.println(noncasi[0]);
}
//7889 

當(dāng)使用AtomicInteger的getAndIncrement方法來(lái)實(shí)現(xiàn)自增之后相當(dāng)于將casi.getAndIncrement()操作變成了原子操作:

private static void AtomicIntegerTest() throws InterruptedException {AtomicInteger casi = new AtomicInteger();casi.set(0);for (int i = 0; i < 10; i++) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {for (int j = 0; j < 1000; j++) {casi.getAndIncrement();}}});thread.start();}while (Thread.activeCount() > 2) {Thread.sleep(10);}System.out.println(casi.get());
}
//10000 

當(dāng)然也可以通過(guò)synchronized關(guān)鍵字來(lái)達(dá)到目的,但CAS操作不需要加鎖解鎖以及切換線程狀態(tài),效率更高。

再來(lái)看看casi.getAndIncrement()具體做了什么,在JDK1.8之前getAndIncrement是這樣實(shí)現(xiàn)的(類似incrementAndGet):

private volatile int value;
public final int incrementAndGet() {for (;;) {int current = get();int next = current + 1;if (compareAndSet(current, next))return next;}
} 

通過(guò)compareAndSet將變量自增,如果自增成功則完成操作,如果自增不成功,則自旋進(jìn)行下一次自增,由于value變量是volatile修飾的,通過(guò)volatile的可見(jiàn)性,每次get()都能獲取到最新值,這樣就保證了自增操作每次自旋一定次數(shù)之后一定會(huì)成功。

JDK1.8中則直接將getAndAddInt方法直接封裝成了原子性的操作,更加方便使用。

public final int getAndIncrement() {return unsafe.getAndAddInt(this, valueOffset, 1);
} 

CAS操作是實(shí)現(xiàn)Java并發(fā)包的基石,他理解起來(lái)比較簡(jiǎn)單但同時(shí)也非常重要。Java并發(fā)包就是在CAS操作和volatile基礎(chǔ)上建立的,下圖中列舉了J.U.C包中的部分類支撐圖:

到此這篇關(guān)于Java synchronized與CAS使用方式詳解的文章就介紹到這了,更多相關(guān)Java synchronized與CAS內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

万州区| 晋中市| 黎川县| 博乐市| 海伦市| 合江县| 台湾省| 友谊县| 白银市| 靖边县| 合阳县| 南投县| 汕头市| 抚松县| 临漳县| 江门市| 桑日县| 明光市| 原阳县| 沁阳市| 南华县| 淮安市| 哈尔滨市| 准格尔旗| 楚雄市| 监利县| 于都县| 运城市| 寻乌县| 和政县| 台州市| 沙洋县| 新源县| 绍兴市| 连云港市| 加查县| 扎兰屯市| 大足县| 华安县| 南丰县| 腾冲县|