【多線程】變量可見性&線程安全

(一)併發中的變量可見性問題

1.變量分類?

全局變量
局部變量

屬性(靜態的、非靜態的) 全局變量
本地變量 局部變量
參數 局部變量

2、如何在多個線程間共享數據?

用全局變量:靜態變量,或共享對象

3.一個變量在線程1中被改變值了,在線程2中能看到該變量的最新值嗎?

import java.util.concurrent TimeUnit
public class VisbilityDemo{
	//狀態標識
	private static  boolean is=true;
	Public static void main(String[] args){
		new Thread(new Runable(){
			public void run(){
				int i=0;
				while(VisibilityDemo.is) {
					i++;
				}
				System.out.println(i);
			}
		}),start();

		try {
				TimeUnit.SECONDS.slee(2);
		}catch (InterruptedException e){
			e.printStackTrace();
		}
		//設置is爲false,使上面的線程結束while循環
		VisibilityDemo.is = false;
		System.out.println("被設置爲false了");
	}
}

線程會停止循環,打印出i的值嗎?不會

*併發的線程能不能看到變量的最新值,這就是併發中的變量可見性問題

怎樣才能可見?

方式一:同步關鍵字:synchronized

在這裏插入圖片描述
#####方式二:volatile

在這裏插入圖片描述

(二)變量可見性、線程安全問題原因

1.Java內存模型

Java內存模型以及操作規範變量

1.共享變量必須存放在主存中;
2.線程有自己的工作內存,線程只可操作自己的工作內存;
3.線程要操作共享變量,需從主存中讀取到工作內存,改變值後需從工作內存同步到主存中。
在這裏插入圖片描述

Java內存模型會帶來什麼問題?

*有變量A,多線程併發對其累加會有什麼問題?如三個線程併發操作變量A,大家讀取A時讀到A=0,
都對A+1,在將值同步會主內存,結果是多少?
結果是1.

這就是線程安全問題。

如何解決線程安全問題?

內存模型也產生了變量可見性問題。

如何讓線程2使用A時看到最新值?

1.線程1修改A後必須馬上同步回主內存
2.線程2使用A前需從主內存讀取到工作內存。

疑問一:使用前不會重新從主內存讀取到工作內存嗎?
疑問二:修改後不會立馬同步會主內存嗎?

2.Java內存模型–同步交互協議

在這裏插入圖片描述

規定了8種原子操作:

1.lock(鎖定):將主內存中的變量鎖定,爲一個線程所獨佔
2.unlock(解鎖):將lock加的鎖定解除,此時其它的線程可以有機會訪問此變量
3.read(讀取):作用於主存變量,將主存中的變量讀到工作內存當中
4.load(載入):作用於工作內存變量,將read讀取的值保存到工作內存中的變量副本中
5.use(使用):作用於工作內存變量,將值傳遞給線程的代碼執行引擎
6.assign(賦值):作用於工作內存變量,將執行引擎處理返回的值重新賦值給變量副本
7.store(存儲):作用於工作內存變量,將變量副本的值傳送到主內存中
8.write(寫入):作用於主內存變量,將store傳送過來的值寫入到主內存的共享變量中

操作規範:

1.將一個變量從主內存複製到工作內存要順序執行read load操作;要將變量從工作內同步
回主內存要順序執行store、write操作。只要求順序執行,不一定是連續執行
2.做了assign操作,必須同步回主存。不能沒做assign,同步回內存。
在這裏插入圖片描述

併發中保持變量可見性的方式:
1.final變量
 //final不可變變量
 private final int var1 = 1;
2.Synchronized
while (VisibilityDemo.is){
	synchronized(this){
		i++;
	}
}
3.用volatile修飾
//狀態標識
private static volatitle boolean is =true;
1synchronized怎樣做到可見性?
1synchronized語義規範:

1.進入同步塊前,先清空工作內存中的共享變量,從主內存中重新加載。
2.解鎖前必須把修改的共享變量同步回主內存

1synchronized如何做到線程安全的?

1.鎖機制保護共享資源,只有獲得鎖的線程纔可操作共享資源;
2.synchronized語義規範保證了修改共享資源後,會同步回主存,就做到了線程安全。

(三)volatile關鍵字解密

1.volatile怎樣做到可見性
volatile語義規範:

1.使用volatile變量時,必須重新從主內存加載,並且read 、load是連續的。
2.修改volatile變量後,必須立馬同步回主內存,並且store、 write是連續的。

volatile能做到線程安全嗎?

1.不能,因爲它沒有鎖機制,線程可併發操作共享資源。

在這裏插入圖片描述

1爲什麼使用volatile?

synchronized可以保證可見性,爲什麼要用volatile?
1.主要原因:使用volatitle比synchronized簡單
2.volatile性能比synchronized稍好。
*如果是保證可見性,用volatile;保證線程安全用synchronized。

volatile還有什麼用途?

volatile可用於限制局部代碼指令重排序
1.線程A

content = initCoutent(); //(1)
islint = true;//(2)

線程B

is(islint){//(3)
    content.operation();//(4)
}

2.jvm優化指令排序後,代碼的執行順序可能如下:
線程A

islint = true;//(1)
content = initCoutent(); //(2)

3.當兩個線程併發執行時,就可能出現線程B中拋空指針異常。
4.當我們在變量上加volatile修飾時,則用到該變量的塊中就不會進行指令重排優化。

volatile的使用場景
volatitle的使用範圍:

1.volatile只可修飾成員變量(靜態的、非靜態的)【全局變量】
2.多線程併發下,才需要使用它。

volatitle典型的應用場景:

1、只有一個修改者,多個使用者,要求保證可見性的場景
2、狀態標識,如示例中介紹的標識
2、數據定期發佈,都個獲取者

在這裏插入圖片描述
volatile保證併發的情況下,不需要太多的線程進入到鎖機制

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章