⑴ java中的volatile關鍵是什麼作用怎麼使它
volatile關鍵字有什麼用?
恐怕比較一下volatile和synchronized的不同是最容易解釋清楚的。volatile是變數修飾符,而synchronized則作用於一段代碼或方法;看如下三句get代碼:
int i1; int geti1() {return i1;}
volatile int i2; int geti2() {return i2;}
int i3; synchronized int geti3() {return i3;}
geti1()得到存儲在當前線程中i1的數值。多個線程有多個i1變數拷貝,而且這些i1之間可以互不相同。換句話說,另一個線程可能已經改變了它線程內的i1值,而這個值可以和當前線程中的i1值不相同。事實上,Java有個思想叫「主」內存區域,這里存放了變數目前的「准確值」。每個線程可以有它自己的變數拷貝,而這個變數拷貝值可以和「主」內存區域里存放的不同。因此實際上存在一種可能:「主」內存區域里的i1值是1,線程1里的i1值是2,線程2里的i1值是3——這在線程1和線程2都改變了它們各自的i1值,而且這個改變還沒來得及傳遞給「主」內存區域或其他線程時就會發生。
而geti2()得到的是「主」內存區域的i2數值。用volatile修飾後的變數不允許有不同於「主」內存區域的變數拷貝。換句話說,一個變數經volatile修飾後在所有線程中必須是同步的;任何線程中改變了它的值,所有其他線程立即獲取到了相同的值。理所當然的,volatile修飾的變數存取時比一般變數消耗的資源要多一點,因為線程有它自己的變數拷貝更為高效。
既然volatile關鍵字已經實現了線程間數據同步,又要synchronized干什麼呢?呵呵,它們之間有兩點不同。首先,synchronized獲得並釋放監視器——如果兩個線程使用了同一個對象鎖,監視器能強制保證代碼塊同時只被一個線程所執行——這是眾所周知的事實。但是,synchronized也同步內存:事實上,synchronized在「主」內存區域同步整個線程的內存。因此,執行geti3()方法做了如下幾步:
1. 線程請求獲得監視this對象的對象鎖(假設未被鎖,否則線程等待直到鎖釋放)
2. 線程內存的數據被消除,從「主」內存區域中讀入(Java虛擬機能優化此步。。。[後面的不知道怎麼表達,汗])
3. 代碼塊被執行
4. 對於變數的任何改變現在可以安全地寫到「主」內存區域中(不過geti3()方法不會改變變數值)
5. 線程釋放監視this對象的對象鎖
因此volatile只是在線程內存和「主」內存間同步某個變數的值,而synchronized通過鎖定和解鎖某個監視器同步所有變數的值。顯然synchronized要比volatile消耗更多資源。
⑵ 誰給解釋下java內存模型讀volatile域時的語義
變數放在主存區上,使用該變數的每個線程,都將從主存區拷貝一份到自己的工作區上進行操作。 volatile, 聲明這個欄位易變(可能被多個線程使用),Java內存模型負責各個線程的工作區與主存區的該欄位的值保持同步,即一致性。 static, 聲明這個欄位是靜態的(可能被多個實例共享),在主存區上該類的所有實例的該欄位為同一個變數,即唯一性。 volatile, 聲明變數值的一致性;static,聲明變數的唯一性。 此外,volatile同步機制不同於synchronized, 前者是內存同步,後者不僅包含內存同步(一致性),且保證線程互斥(互斥性)。 static 只是聲明變數在主存上的唯一性,不能保證工作區與主存區變數值的一致性;除非變數的值是不可變的,即再加上final的修飾符,否則static聲明的變數,不是線程安全的
⑶ 關於java中的volatile
第一個不是線程安全的,因為當一個線程執行同步的修改方法時,另一個線程還是可以執行get方法的。
另外兩個都是線程安全的,volatile變數一個時間只有一個線程可以訪問。
每個對象只有一個對象鎖,當一個線程訪問其中一個sychronized實例方法時,該方法獲得對象鎖,其他所有的sychronized方法都無法被其他線程訪問,所以最後一個也是線程安全的。
其實第二個和最後一個是等價的。第二個其實連sychronized都可以不要
⑷ java中volatile修飾的變數有什麼特徵
volatile是一個類型修飾符,它是被設計用來修飾被不同線程訪問和修改的變數,可以被非同步的線程所修改。
final必須對它賦予初值並且不能修改它。
對比就知道兩個修飾符是沖突的,放一起是要干什麼呢?
⑸ java 程序中怎麼保證多線程的運行安全
2.1.讀一致性
Java 中針對上述「讀不安全」的問題提供了關鍵字 volatile 來解決問題,被 volatile 修飾的成員變數,在內容發生更改的時候,會通知所有線程去主內存更新最新的值,這樣就解決了讀不安全的問題,實現了讀一致性。
但是,讀一致性是無法解決寫一致性的,雖然能夠使得每個線程都能及時獲取到最新的值,但是1.1中的寫一致性問題還是會存在。
既然如此,Java 為啥還要提供 volatile 關鍵字呢?這並非多餘的存在,在某些場景下只需要讀一致性的話,這個關鍵字就能夠滿足需求而且性能相對還不錯,因為其他的能夠保證「讀寫」都一直的辦法,多多少少存在一些犧牲。
2.2.寫一致性
Java 提供了三種方式來保證讀寫一致性,分別是互斥鎖、自旋鎖、線程隔離。
2.2.1.互斥鎖
互斥鎖只是一個鎖概念,在其他場景也叫做獨占鎖、悲觀鎖等,其實就是一個意思。它是指線程之間是互斥的,某一個線程獲取了某個資源的鎖,那麼其他線程就只能睡眠等待。
在 Java 中互斥鎖的實現一般叫做同步線程鎖,關鍵字 synchronized,它鎖住的范圍是它修飾的作用域,鎖住的對象是:當前對象(對象鎖)或類的全部對象(類鎖)——鎖釋放前,其他線程必將阻塞,保證鎖住范圍內的操作是原子性的,而且讀取的數據不存在一致性問題。
對象鎖:當它修飾方法、代碼塊時,將會鎖住當前對象
類鎖:修飾類、靜態方法時,則是鎖住類的所有對象
注意:鎖住的永遠是對象,鎖住的范圍永遠是 synchronized 關鍵字後面的花括弧劃定的代碼域。
2.2.2.自旋鎖
自旋鎖也只是一個鎖概念,在其他場景也叫做樂觀鎖等。
自旋鎖本質上是不加鎖,而是通過對比舊數據來決定是否更新:
⑹ java之用volatile和不用volatile的區別
當我們聲明共享變數為volatile後,對這個變數的讀/寫將會很特別。理解volatile特性的一個好方法是:把對volatile變數的單個讀/寫,看成是使用同一個監視器鎖對這些單個讀/寫操作做了同步。下面我們通過具體的示例來說明,請看下面的示例代碼:
class VolatileFeaturesExample {
volatile long vl = 0L; //使用volatile聲明64位的long型變數
public void set(long l) {
vl = l; //單個volatile變數的寫
}
public void getAndIncrement () {
vl++; //復合(多個)volatile變數的讀/寫
}
public long get() {
return vl; //單個volatile變數的讀
}
}
假設有多個線程分別調用上面程序的三個方法,這個程序在語意上和下面程序等價:
class VolatileFeaturesExample {
long vl = 0L; // 64位的long型普通變數
public synchronized void set(long l) { //對單個的普通 變數的寫用同一個監視器同步
vl = l;
}