基于多線(xiàn)程并發(fā)的常見(jiàn)問(wèn)題(詳解)
一 概述
1.volatile
保證共享數(shù)據(jù)一旦被修改就會(huì)立即同步到共享內(nèi)存(堆或者方法區(qū))中。
2.線(xiàn)程訪(fǎng)問(wèn)堆中數(shù)據(jù)的過(guò)程
線(xiàn)程在棧中建立一個(gè)數(shù)據(jù)的副本,修改完畢后將數(shù)據(jù)同步到堆中。
3.指令重排
為了提高執(zhí)行效率,CPU會(huì)將沒(méi)有依賴(lài)關(guān)系的指令重新排序。如果希望控制重新排序,可以使用volatile修飾一個(gè)變量,包含該變量的指令前后的指令各自獨(dú)立排序,前后指令不能交叉排序。
二 常見(jiàn)問(wèn)題及應(yīng)對(duì)
1.原子性問(wèn)題
所謂原子性,指的是一個(gè)操作不可中斷,即在多線(xiàn)程并發(fā)的環(huán)境下,一個(gè)操作一旦開(kāi)始,就會(huì)在同一個(gè)CPU時(shí)間片內(nèi)執(zhí)行完畢。如果同一個(gè)線(xiàn)程的多個(gè)操作在不同的CPU時(shí)間片上執(zhí)行,由于中間出現(xiàn)停滯,后面的操作在執(zhí)行時(shí)可能某個(gè)共享數(shù)據(jù)被其他線(xiàn)程修改,而該修改并未同步到當(dāng)前線(xiàn)程中,導(dǎo)致當(dāng)前線(xiàn)程操作的數(shù)據(jù)與實(shí)際不符,這種由于執(zhí)行不連貫導(dǎo)致的數(shù)據(jù)不一致問(wèn)題被稱(chēng)作原子性問(wèn)題。
2.可見(jiàn)性問(wèn)題
可見(jiàn)性問(wèn)題的出現(xiàn)與線(xiàn)程訪(fǎng)問(wèn)共享數(shù)據(jù)的方式有關(guān)。線(xiàn)程訪(fǎng)問(wèn)堆(方法區(qū))中的變量時(shí),先在棧中建立一個(gè)變量的副本,修改后再同步到堆中。如果一個(gè)線(xiàn)程剛建立副本,這時(shí)另一線(xiàn)程修改了變量,尚未同步到堆中,這時(shí)就會(huì)出現(xiàn)兩個(gè)線(xiàn)程操作同一變量的同一種狀態(tài)的現(xiàn)象,比如i=9,變量i的初始值為9,每一個(gè)線(xiàn)程的操作都是減1。兩個(gè)線(xiàn)程A與B同時(shí)訪(fǎng)問(wèn)變量,B先執(zhí)行i-1,在將結(jié)果i=8同步到堆中前,A線(xiàn)程也執(zhí)行i-1,這時(shí)i=9的狀態(tài)就被執(zhí)行兩次,出現(xiàn)線(xiàn)程安全問(wèn)題。
線(xiàn)程安全問(wèn)題產(chǎn)生的原因:一個(gè)線(xiàn)程對(duì)共享數(shù)據(jù)的修改不能立即為其他線(xiàn)程所見(jiàn)。
volatile提供了一種解決方案:
一旦一個(gè)線(xiàn)程修改了被volatile修飾的共享數(shù)據(jù),這種修改就會(huì)立即同步到堆中,這樣其他數(shù)據(jù)從堆中訪(fǎng)問(wèn)共享數(shù)據(jù)時(shí)始終獲得的是在多個(gè)線(xiàn)程中的最新值。
volatile的缺陷:
volatile只能保證一個(gè)線(xiàn)程從堆中獲取數(shù)據(jù)時(shí)獲取的是當(dāng)前所有線(xiàn)程中的最新值,假如一個(gè)線(xiàn)程已經(jīng)從堆中復(fù)制了數(shù)據(jù),在操作完成前,其他線(xiàn)程修改了數(shù)據(jù),修改后的數(shù)據(jù)并不會(huì)同步到當(dāng)前線(xiàn)程中。
3.有序性問(wèn)題
為了提高執(zhí)行效率,CPU會(huì)對(duì)那些沒(méi)有依賴(lài)關(guān)系的指令重新排序,重新排序后的執(zhí)行結(jié)果與順序執(zhí)行結(jié)果相同。
例如,在源代碼中:
int i=0; int y=1;
CPU在執(zhí)行時(shí)可能先執(zhí)行“int y=1;”,接著執(zhí)行“int i=0;”,執(zhí)行結(jié)果與順序執(zhí)行結(jié)果相同。
指令重排在單線(xiàn)程環(huán)境下是安全的,在多線(xiàn)程環(huán)境下就可能出現(xiàn)問(wèn)題。比如:
線(xiàn)程A:
s=new String("sssss");//指令1
flag=false;//
指令2線(xiàn)程B:
while(flag){
doSome();
}
s.toUpperCase();//指令3如果線(xiàn)程A順序執(zhí)行,即執(zhí)行指令1,再執(zhí)行指令2,線(xiàn)程B的執(zhí)行不會(huì)出現(xiàn)問(wèn)題。指令重排后,假如線(xiàn)程A先執(zhí)行指令2,這時(shí)flag=true,切換到線(xiàn)程2,終止循環(huán),執(zhí)行指令3,由于s對(duì)象尚未創(chuàng)建就會(huì)出現(xiàn)空指針異常。
有序性問(wèn)題產(chǎn)生的原因:
一個(gè)線(xiàn)程對(duì)其他線(xiàn)程對(duì)共享數(shù)據(jù)的修改操作有順序要求,比如線(xiàn)程B要求線(xiàn)程A先執(zhí)行指令1,再執(zhí)行指令2,由于指令重排,實(shí)際并未按照要求的順序執(zhí)行,這時(shí)就出現(xiàn)了線(xiàn)程安全問(wèn)題。
解決思路:
1.利用同步機(jī)制,使得同一時(shí)間只有一個(gè)線(xiàn)程可以訪(fǎng)問(wèn)共享數(shù)據(jù),效率低。
2.使用volatile,一個(gè)指令包含volatile修飾的變量,那么這條指令的執(zhí)行順序不變,該指令前后的指令可以各自獨(dú)立重排,無(wú)法交叉重排。
以上這篇基于多線(xiàn)程并發(fā)的常見(jiàn)問(wèn)題(詳解)就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java中Mybatis,SpringMVC,Spring的介紹及聯(lián)系
這篇文章主要為大家詳細(xì)介紹了Java中Mybatis,SpringMVC,Spring的介紹及聯(lián)系,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10
JAVA錯(cuò)誤類(lèi)結(jié)果類(lèi)和分頁(yè)結(jié)果類(lèi)代碼詳解
這篇文章主要介紹了JAVA錯(cuò)誤類(lèi)結(jié)果類(lèi)和分頁(yè)結(jié)果類(lèi)代碼詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02
idea手動(dòng)執(zhí)行maven命令的三種實(shí)現(xiàn)方式
這篇文章主要介紹了idea手動(dòng)執(zhí)行maven命令的三種實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08
JAXB簡(jiǎn)介_(kāi)動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要為大家詳細(xì)介紹了JAXB簡(jiǎn)介的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
解決Spring導(dǎo)出可以運(yùn)行的jar包問(wèn)題
最近需要解決Maven項(xiàng)目導(dǎo)入可執(zhí)行的jar包的問(wèn)題,如果項(xiàng)目不包含Spring,那么使用mvn assembly:assembly即可,這篇文章主要介紹了Spring導(dǎo)出可以運(yùn)行的jar包,需要的朋友可以參考下2023-03-03

