Java多線程并發(fā)編程 并發(fā)三大要素
一、原子性
原子,一個(gè)不可再被分割的顆粒。原子性,指的是一個(gè)或多個(gè)不能再被分割的操作。
int i = 1; // 原子操作
i++; // 非原子操作,從主內(nèi)存讀取 i 到線程工作內(nèi)存,進(jìn)行 +1,再把 i 寫(xiě)到朱內(nèi)存。
雖然讀取和寫(xiě)入都是原子操作,但合起來(lái)就不屬于原子操作,我們又叫這種為“復(fù)合操作”。
我們可以用synchronized 或 Lock 來(lái)把這個(gè)復(fù)合操作“變成”原子操作。
例子:
private synchronized void increase(){
i++;
}
或
private int i = 0;
Lock mLock = new ReentrantLock();
private void increase() {
mLock.lock();
try {
i++;
} finally{
mLock.unlock();
}
}
這樣我們就可以把這個(gè)一個(gè)方法看做一個(gè)整體,一個(gè)不可分割的整體。
除此之前,我們還可以用java.util.concurrent.atomic里的原子變量類(lèi),可以確保所有對(duì)計(jì)數(shù)器狀態(tài)訪問(wèn)的操作都是原子的。
例子:
AtomicInteger mAtomicInteger = new AtomicInteger(0);
private void increase(){
mAtomicInteger.incrementAndGet();
}
二、可見(jiàn)性
當(dāng)多線程訪問(wèn)某一個(gè)(同一個(gè))變量時(shí),其中一條線程對(duì)此變量作出修改,其他線程可以立刻讀取到最新修改后的變量。
int i = 0;
// 線程 1 執(zhí)行
i++;
// 線程 2 執(zhí)行
System.out.print("i=" + i);
即使是在執(zhí)行完線程里的 i++ 后再執(zhí)行線程 2,線程 2 的輸入結(jié)果也會(huì)有 2 個(gè)種情況,一個(gè)是 0 和 1。
因?yàn)?i++ 在線程 1(CPU1)中做完了運(yùn)算,并沒(méi)有立刻更新到主內(nèi)存當(dāng)中,而線程 2(CPU2)就去主內(nèi)存當(dāng)中讀取并打印,此時(shí)打印的就是 0。
synchronized和Lock能夠保證可見(jiàn)性。
另外volatile關(guān)鍵字也可以解決這個(gè)問(wèn)題(下一篇會(huì)講到)。
三、有序性
我們都知道處理器為了擁有更好的運(yùn)算效率,會(huì)自動(dòng)優(yōu)化、排序執(zhí)行我們寫(xiě)的代碼,但會(huì)確保執(zhí)行結(jié)果不變。
例子:
int a = 0; // 語(yǔ)句 1 int b = 0; // 語(yǔ)句 2 i++; // 語(yǔ)句 3 b++; // 語(yǔ)句 4
這一段代碼的執(zhí)行順序很有可能不是按上面的 1、2、3、4 來(lái)依次執(zhí)行,因?yàn)?1 和 2 沒(méi)有數(shù)據(jù)依賴(lài),3 和 4 沒(méi)有數(shù)據(jù)依賴(lài), 2、1、4、3 這樣來(lái)執(zhí)行可以嗎?完全沒(méi)問(wèn)題,處理器會(huì)自動(dòng)幫我們排序。
在單線程看來(lái)并沒(méi)有什么問(wèn)題,但在多線程則很容易出現(xiàn)問(wèn)題。
再來(lái)個(gè)例子:
// 線程 1
init();
inited = true;
// 線程 2
while(inited){
work();
}
init(); 與 inited = true; 并沒(méi)有數(shù)據(jù)的依賴(lài),在單線程看來(lái),如果把兩句的代碼調(diào)換好像也不會(huì)出現(xiàn)問(wèn)題。
但此時(shí)處于一個(gè)多線程的環(huán)境,而處理器真的把這兩句代碼重新排序,那問(wèn)題就出現(xiàn)了,若線程 1 先執(zhí)行 inited = true; 此時(shí),init() 并沒(méi)有執(zhí)行,線程 2 就已經(jīng)開(kāi)始調(diào)用 work() 方法,此時(shí)很可能造成一些奔潰或其他 BUG 的出現(xiàn)。
synchronized和Lock能確保原子性,能讓多線程執(zhí)行代碼的時(shí)候依次按順序執(zhí)行,自然就具有有序性。
而volatile關(guān)鍵字也可以解決這個(gè)問(wèn)題,volatile 關(guān)鍵字可以保證有序性,讓處理器不會(huì)把這行代碼進(jìn)行優(yōu)化排序。
- Java并發(fā)編程示例(一):線程的創(chuàng)建和執(zhí)行
- Java并發(fā)編程示例(六):等待線程執(zhí)行終止
- Java并發(fā)編程之顯示鎖ReentrantLock和ReadWriteLock讀寫(xiě)鎖
- Java并發(fā)編程之柵欄(CyclicBarrier)實(shí)例介紹
- Java并發(fā)編程示例(二):獲取和設(shè)置線程信息
- Java并發(fā)編程示例(七):守護(hù)線程的創(chuàng)建和運(yùn)行
- Java并發(fā)編程示例(十):線程組
- Java并發(fā)編程總結(jié)——慎用CAS詳解
- 理解Java多線程之并發(fā)編程
相關(guān)文章
Java中==與equals()及hashcode()三者之間的關(guān)系詳解
最近也是在讀Hollis的《深入理解Java核心技術(shù)》里面一節(jié)講到了equals()和hashcode()的關(guān)系,對(duì)于這個(gè)高頻面試點(diǎn),咱們需要認(rèn)真理清一下幾者之間的關(guān)系2022-10-10
spring+maven實(shí)現(xiàn)發(fā)送郵件功能
這篇文章主要為大家詳細(xì)介紹了spring+maven實(shí)現(xiàn)發(fā)送郵件功能,利用spring提供的郵件工具來(lái)發(fā)送郵件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07
metershpere實(shí)現(xiàn)調(diào)用自定義jar包中的方法
Java基礎(chǔ)之Bean的創(chuàng)建、定位和使用
詳解IDEA用maven創(chuàng)建springMVC項(xiàng)目和配置
mybatis關(guān)系映射之一對(duì)多和多對(duì)一

