欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報邀請碼778899分享:jvm 【JavaEE】

柚子快報邀請碼778899分享:jvm 【JavaEE】

http://yzkb.51969.com/

目錄

1.?線程不安全問題

2. 線程不安全的原因

3. 解決線程不安全問題

1.?線程不安全問題

線程安全問題是多線程編程必須考慮的重要問題,也因為其難以理解與處理,故而程序員也嘗試發(fā)明更多的編程模型來處理并發(fā)編程,如多進程、多線程、actor、csp等等;

我們知道,操作系統(tǒng)調(diào)度線程是搶占式執(zhí)行,這樣的隨機性可能會導致程序執(zhí)行出現(xiàn)一些bug,如果由于這樣的調(diào)度的隨機性使得代碼出現(xiàn)了bug,則認為代碼是不安全的,如果沒有引入bug,則認為代碼是安全的;

線程不安全的典型案例:使用兩個線程對同一個整型變量進行自增操作,每個線程自增五萬次:

class Counter{

//保存兩個線程要自增的變量

public int count = 0;

public void increase(){

count++;

}

}

public class Demo1 {

private static Counter counter = new Counter();

public static void main(String[] args) throws InterruptedException {

Thread t1 = new Thread(()->{

for(int i=0;i<50000;i++){

counter.increase();

}

});

Thread t2 = new Thread(()->{

for(int i=0;i<50000;i++){

counter.increase();

}

});

t1.start();

t2.start();

//在main線程中打印兩個線程自增結(jié)束后得到的count結(jié)果

//t1、t2執(zhí)行結(jié)束后再打印count結(jié)果

t1.join();

t2.join();

System.out.println(counter.count);

}

}

運行三次,輸出結(jié)果與預期并不相符且多次運行多次不同:

? ??? ????

注:1.t1.join()與t2.join()誰先誰后均可:

線程是隨機調(diào)度的,t1、t2線程的結(jié)束前后是未知的,

如果t1先結(jié)束,則先令main線程等待t1結(jié)束,待t1結(jié)束后再令main線程等待t2結(jié)束;

如果t2先結(jié)束,仍先令main等待t1結(jié)束,t2結(jié)束了t1還未結(jié)束,main線程仍然在等待t1結(jié)束,等t1結(jié)束后,t2已經(jīng)結(jié)束了,則此時t2.jion()立即返回;

2.站在CPU角度來看,count++實際上是3個CPU指令:

第一步:將內(nèi)存中count值加載到CPU寄存器中;(load)

第二步:寄存器中的值將其+1;(add)

第三步:把寄存器中的值寫回到內(nèi)存的count中;(save)

由于搶占式執(zhí)行,兩個線程同時執(zhí)行這三個指令的時候順序上充滿了隨機性,只有當兩個線程的三條指令串型執(zhí)行的時候才會符合預期,只要三條指令出現(xiàn)交錯,就會出現(xiàn)錯誤,如:

2. 線程不安全的原因

(1)根本原因:線程是搶占式執(zhí)行,線程間的調(diào)度充滿隨機性;

(2)修改共享數(shù)據(jù):多個線程對同一個變量進行修改操作,才會導致線程不安全問題;

當多個線程分別對不同的多個變量進行操作,或是多個線程對同一個變量進行讀操作,都不會導致線程不安全問題;

(3)操作的原子性問題:針對變量的操作不是原子性的,就會導致線程不安全問題,如上文示例中,自增操作其實是3條指令;

當操作是原子性的,如讀取變量的值就只對應一條機器指令,就不會導致線程不安全問題;

(4)內(nèi)存可見性問題:java編譯器的優(yōu)化操作使得在某些情況下線程之間出現(xiàn)信息不同步問題:

如線程t1一直在高速循環(huán)進行讀操作,線程t2不定時進行修改操作,此時由于t1的高速訪問可能無果,就會停止將數(shù)據(jù)從內(nèi)存中讀至寄存器中再進行讀取,而直接從寄存器中讀取,此時若t2線程進行修改操作,就會由于內(nèi)存可見性問題而使兩個線程信息不同步,出現(xiàn)安全問題,示例代碼如下:

import java.util.Scanner;

public class Demo2 {

private static int isQuit = 0;

public static void main(String[] args) {

Thread t = new Thread(()->{

while(0 == isQuit){

}

System.out.println("Thread t has finished.");

});

t.start();

Scanner scanner = new Scanner(System.in);

System.out.println("Please input the value of isQuit: ");

isQuit = scanner.nextInt();

System.out.println("Thread main has finished.");

}

}

輸出結(jié)果為:

并未輸出"Thread t has finished."說明t線程并未結(jié)束;?

(5)指令重排序問題:指令重排序也是編譯器優(yōu)化的一種操作,編譯器在某些情況下可能調(diào)整代碼的先后順序來提高程序的效率,單線程通常不會出現(xiàn)問題,但在多線程代碼中,可能就會誤判導致線程安全問題;

3. 解決線程不安全問題

對應上文的線程不安全問題原因,思考解決線程不安全問題的方法:

(1)線程調(diào)度的隨機性問題:無法從代碼層面進行改進的;

(2)多線程修改同一變量問題:部分情況下可調(diào)整代碼結(jié)構(gòu),使不同線程操作不同變量;

(3)變量操作的原子性問題:加鎖操作將多個操作打包為一個原子性操作;

(4)內(nèi)存可見性問題:

① 使用synchronized關(guān)鍵字可以保證內(nèi)存可見性,被synchronied修飾的代碼塊,相當于手動禁止了編譯器的優(yōu)化;

② 使用volatile關(guān)鍵字可以保證內(nèi)存可見性,禁止編譯器做出上述優(yōu)化:

import java.util.Scanner;

public class Demo2 {

private static volatile int isQuit = 0;

public static void main(String[] args) {

Thread t = new Thread(()->{

while(0 == isQuit){

}

System.out.println("Thread t has finished.");

});

t.start();

Scanner scanner = new Scanner(System.in);

System.out.println("Please input the value of isQuit: ");

isQuit = scanner.nextInt();

System.out.println("Thread main has finished.");

}

}

此時輸出結(jié)果為:

??

(5)指令重排序問題:synchronized關(guān)鍵字可以禁止指令重排序;

注:synchronized解決多線程修改同一變量問題代碼示例:

使用鎖后,就將線程間亂序的并發(fā)變成了一個串型操作,并發(fā)性降低但會更安全;

雖然效率有所降低但相較于單線程程序,還是能分擔步驟壓力,效率還是較高的;

java中加鎖的方式有很多種,最常使用的是synchronized關(guān)鍵字:

class Counter{

public int count=0;

synchronized public void increase(){

count++;

}

}

public class Demo1 {

private static Counter counter = new Counter();

public static void main(String[] args) throws InterruptedException {

Thread t1 = new Thread(()->{

for(int i=0;i<50000;i++){

counter.increase();

}

});

Thread t2 = new Thread(()->{

for(int i=0;i<50000;i++){

counter.increase();

}

});

t1.start();

t2.start();

t1.join();

t2.join();

System.out.println(counter.count);

}

}

輸出結(jié)果為:

??

注:(1)在increase()方法前加上synchronized修飾,此時進入方法就會自動加鎖,離開方法就會自動解鎖;

(2)當給一個線程加鎖成功時,其他線程嘗試加鎖就會觸發(fā)阻塞等待,此時對應的線程就處于clocked狀態(tài);

(3)阻塞狀態(tài)會一直持續(xù)到占用鎖的線程解鎖為止,時間軸縮略圖如下:

?(3)synchronized可以保證操作的原子性,保證內(nèi)存可見性,還可以禁止指令重排序;

柚子快報邀請碼778899分享:jvm 【JavaEE】

http://yzkb.51969.com/

精彩文章

評論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。

轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/19519956.html

發(fā)布評論

您暫未設置收款碼

請在主題配置——文章設置里上傳

掃描二維碼手機訪問

文章目錄