程序,糾集到底就是對內存數據的操作,並把計算的結果持久話. 爭議
JAVA中執行的最小單位是線程.JVM實現了各個CPU,操作系統等的差異. 線程的運行模型最終可以抽象的看成如下:
每一條線程都有自己的work memory, 而且共享一個main memory.
JMM的主要問題如下:
原子性,原子級別的操作,每個線程運行時是相互獨立,包括里面未聲明為volatile的變量都是獨立一份,但會進行work memory 和 main memory的同步;
可見性, 線程間的通訊. 即主內存的變量可見的,把值從work memory同步到main memory 進行線程間的通訊,通過synchronize或者volatile可靠性;
有序性,這里主要針對讀和寫及同步主內存的的有序, 線程的操作一般是
read and load 從主存復制變量到當前工作內存
use and assign 執行代碼,改變共享變量值
store and write 用工作內存數據刷新主存相關內容
其中read and load和store and write 都是多次運行,取決於JVM
1,每一條線程中運行的變量只是從main memory 的copy,存放於緩存之中
常見的並發問題, 在內存中各自加,未同步到主內存,比如
package org.benson.threadgroup; import java.util.concurrent.CountDownLatch; /** * TODO share the thread memory operation * @author Benson QQ 107966750 * */ class Data{ public static int count=0; } public class TreadTest extends Thread{ private CountDownLatch countDCH=null; public TreadTest(CountDownLatch countDCH) { this.countDCH=countDCH; } @Override public void run() { for(int i=0;i<10000;i++) // synchronized (Data.class) { Data.count++; // } countDCH.countDown(); } public static void main(String[] args) throws InterruptedException { final int threadSize=10; CountDownLatch countDCH=new CountDownLatch(threadSize); for(int i=0;i<threadSize;i++) new Thread(new TreadTest(countDCH)).start(); countDCH.await(); System.out.println(Data.count); } }
輸出
91660
這里由於讀寫沒及時有序的同步到主內存造成,小於預期值100000,釋放同步鎖后正常
當第二線程運行完畢時始終輸出正確計算值,原理看synchronize關鍵詞
2,JAVA調用代碼的次序是無序性的.(如DCL等問題)
一,bad實踐,依靠變量線程通訊,這個不容易重現,但執行了N遍還是正確的,取決於JVM的心情問題,代碼如下
package org.benson.threadgroup; /** * TODO share load variable sort * @author Benson QQ107966750 * */ class Process4SortA{ public int variableA=0; public Process4SortA() { variableA=100; } } class Process4SortB extends Thread{ public boolean startFlag=false; public int variableA; public void test4SortRun(){ //do something another stuff variableA=100; startFlag=true; //maybe will process this code before the variableA=100 } @Override public void run() { while(true){ if(startFlag){ System.out.println(variableA); //do something another stuff break; } } } } public class TreadTestSort { public static void main(String[] args) throws Exception { Process4SortB proB=new Process4SortB(); proB.start(); proB.test4SortRun(); } }
代碼輸出可能會等於0. 因為在方法test4SortRun()里,執行的順序是不一定的
二,bad實踐,DCL的問題,這里引出最常見的,一個double checked load(DCL) 問題,也是由於無序造成的
class Foo { private Resource res = null ; public Resource getResource() { if (res == null ) res = new Resource(); return res; } }
相關資料很多,其實可以把
res = new Resource();
看成
Resource()
res = Resource地址引用;
這個是正確執行順序,因為無序的原因可能是
res = Resource地址引用;
Resource()
所以其他得到了res!=null,就執行了,構造函數根本沒執行完
三,bad實踐,單例模式
這個和DCL類似
java Singleton 幾種方式解析(實在找不到原帖了,BS下轉貼不貼出地址的)
當然最后一個volatile的可以,因為volatile每次都是讀到主內存中最新的值
相關關鍵詞
synchronize
1,獲取並掛起monitor,
2,從main memory copy最新的值
3,執行
4,從work memory copy最新的值到main memory
5,釋放monitor
final
不可變,只能在初始化時賦值,
但非靜態可以在構造方法中賦值,應用時時注意順序,如果下面這樣調用 final變量也成了可變了
用如下代碼證明
package org.benson;
/**
* TODO to share the java load class sort
* @author Benson QQ 107966750
*
*/
class Base{
public Base() {
System.out.println("init base,the variableA is "+((Child)this).variableA);
}
public void display(){
System.out.println("display in base,the variableA is "+((Child)this).variableA);
}
}
class Child extends Base{
final int variableA;
public Child(){
variableA=100;
System.out.println("init child,the variableA is "+this.variableA);
this.display();
}
}
public class Test4Sort {
public static void main(String[] args) {
new Child();
}
}
結果如下
init base,the variableA is 0
init child,the variableA is 100
display in base,the variableA is 100
這樣final值也成了可變了
JAVA的初始化順序 base and child class adress,static-->base class variable-->base class constructor-->child class variable-->child class constructor
volatile
在線程中,讀和寫都是原子性的,volatile的最佳實踐就是用synchronize加鎖寫(或者用不依賴當前volatile的值的寫),然后讀可以不用鎖,可以保證讀到最新值,類似一個共享鎖的實現
1,保證的是線程的可見性,即每次得到的都是最新值,顯然寫入是不保證的,所以不能做計數器
2,如果一個線程里讀和寫同時發生,它可以保證寫先於讀,比如 a=new instance(); 可以保證先把instance()初始化完畢后賦值給a,返回完整的instance
如一個線程的寫入遠少於,一個線程不停的讀取,就可以對變量進行volatile了,能保證讀到的是最新值(但寫入必須不依賴當前值),這種情況可以考慮和synchronize同時用,參考從ConcurrentHashMap類學習高並發程序的設計思路【深入JDK源碼】
參考資料
Java內存模型(找不到原帖)
Java多線程發展簡史