java并发进程共享变量_JAVA并发编程学习:共享对象
可見性
在一個(gè)單線程程序中,如果向一個(gè)變量先寫入值,然后在沒有寫干涉的情況下讀取這個(gè)變量,會(huì)得到相同的返回值。但是當(dāng)讀和寫發(fā)生在不同的線程中時(shí),就不能保證讀線程及時(shí)地讀取其他線程寫入的值。在JAVA中所有實(shí)例域、靜態(tài)域和數(shù)組元素存儲(chǔ)在堆內(nèi)存中,堆內(nèi)存在線程之間共享,局部變量,方法定義參數(shù)和異常處理器參數(shù)不會(huì)在線程之間共享,它們不會(huì)有內(nèi)存可見性問題,也不受內(nèi)存模型的影響。為了確保跨線程寫入的內(nèi)存可見性,必須使用同步機(jī)制
下例是主線程和讀線程兩個(gè)線程訪問共享變量ready和number。主線程啟動(dòng)讀線程,然后把number的值設(shè)為42,ready的值賦為true。讀線程進(jìn)行循環(huán),直到發(fā)現(xiàn)ready的值變?yōu)閠rue,然后打印出number的值。雖然看起來會(huì)輸出42,但事實(shí)上,它很有可能打印出0,或者根本不會(huì)終止。這是因?yàn)樗鼪]有使用恰當(dāng)?shù)耐綑C(jī)制,沒能保證主線程寫入ready和number的值對(duì)讀線程是可見的
package com.henrysun.javaSE.bfbc;
/**
* 在沒有同步的情況下共享變量(不要這樣做)
* 重排序現(xiàn)象
* @author Sam Flynn
*
*/
public class GongXiangBianLiangReordering {
private static boolean ready;
private static intnumber;
private static class ReaderThread extends Thread
{
public void run()
{
while(!ready)
{
Thread.yield();
}
System.out.println(number);
}
}
/**
* @param args
*/
public static void main(String[] args) {
new ReaderThread().start();
number=42;
ready=true;
}
}
重排序現(xiàn)象
上面的例子中,可能會(huì)打印0,因?yàn)樵缭趯?duì)number賦值之前,主線程就已經(jīng)寫入ready并使之對(duì)讀線程可見,這叫做“重排序(reordering)”現(xiàn)象。
這里處理器A和處理器B可以同時(shí)把共享變量寫入自己的寫緩沖區(qū)(A1,B1),然后從內(nèi)存中讀取另一個(gè)共享變量(A2,B2),最后才把自己寫緩存區(qū)中保存的臟數(shù)據(jù)刷新到內(nèi)存中(A3,B3)。當(dāng)以這種時(shí)序執(zhí)行時(shí),程序就可以得到x = y = 0的結(jié)果
鎖和可見性
鎖不僅僅是關(guān)于同步互斥的,也是關(guān)于內(nèi)存可見的。當(dāng)線程A執(zhí)行一個(gè)同步塊時(shí),線程B也隨后進(jìn)入了被同一個(gè)鎖監(jiān)視的同步塊中,這時(shí)可以保證,在鎖釋放之前對(duì)A的可見的變量的值,B獲得鎖之后同樣是可見的。換句話說,當(dāng)B執(zhí)行到A相同的鎖監(jiān)視的同步塊時(shí),A在同步塊之中或之前所做的每件事,對(duì)B都是可見的。為了保證所有線程都能夠看到共享的,可變變量的最新值,讀取和寫入線程必須使用公共的鎖進(jìn)行同步
Volatile變量
當(dāng)一個(gè)域聲明為volatile類型后,編譯器與運(yùn)行時(shí)會(huì)監(jiān)視這個(gè)變量:它是共享的,而且對(duì)它的操作不會(huì)與其他的內(nèi)存操作一起被重排序。volatile變量不會(huì)緩存在寄存器或者緩存在其他對(duì)處理器隱藏的地方。所以,讀一個(gè)volatile類型的變量時(shí),總會(huì)返回由某一線程寫入的最新值
但是volatile變量的操作不會(huì)加鎖,也就不會(huì)引起執(zhí)行線程的阻塞,所以它只是輕量級(jí)的同步機(jī)制,正確使用volatile變量的方式包括:用于確保它們所引用的對(duì)象狀態(tài)的可見性,或者用于標(biāo)識(shí)重要的生命周期事件(比如初始化或關(guān)閉)的發(fā)生,即通常被當(dāng)作標(biāo)識(shí)完成、中斷、狀態(tài)的標(biāo)記使用
盡管volatile也可以用來標(biāo)識(shí)其他類型的狀態(tài)信息,但是決定這樣做之前請(qǐng)格外小心。比如,volatile的語(yǔ)義不足以使自增操作(count++)原子化,除非你能保證只有一個(gè)線程對(duì)變量執(zhí)行寫操作。加鎖可以保證可見性與原子性,而volatile變量只能保證可見性
只有滿足了下面的標(biāo)準(zhǔn),才能使用volatile變量
寫入變量時(shí)并不依賴變量的當(dāng)前值;或者能夠確保只有單一的線程修改變量的值
變量不需要與其他的狀態(tài)變量共同參與不變約束
而且,訪問變量時(shí),沒有其他的原因需要加鎖
ThreadLocal
總結(jié)
以上是生活随笔為你收集整理的java并发进程共享变量_JAVA并发编程学习:共享对象的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 安装后找不到文件_(已解决)j
- 下一篇: Java wait forever_彻底