在java中可以有很多方法來保證線程安全,比如使用同步方法、同步塊,使用原子類(atomic concurrent classes),實(shí)現(xiàn)并發(fā)鎖,使用volatile關(guān)鍵字,使用不變類和線程安全類。
這里是最基礎(chǔ)的線程安全教程
實(shí)際上在volatile的使用上很容易有誤解,以為volatile就可以做原子操作,實(shí)際不然。Volatile 變量具有 synchronized
的可見性特性,但是不具備原子特性。這就是說線程能夠自動發(fā)現(xiàn) volatile 變量的最新值。Volatile 變量可用于提供線程安全,但是只能應(yīng)用于非常有限的一組用例:多個變量之間或者某個變量的當(dāng)前值與修改后值之間沒有約束。因此,單獨(dú)使用 volatile 還不足以實(shí)現(xiàn)計數(shù)器、互斥鎖或任何具有與多個變量相關(guān)的不變式(Invariants)的類(例如 “start <=end”)。
對于volatile修飾的變量,jvm虛擬機(jī)只是保證從主內(nèi)存加載到線程工作內(nèi)存的值是最新的。
直接上代碼:
import java.util.concurrent.atomic.AtomicInteger;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class Counter { public static int count = 0; //對于值引用來說,多線程操作的是變量的副本,操作完后刷新到主存中.所以不具有原子性。 //錯誤的volatile使用方法,volatil只是直接進(jìn)行內(nèi)存地址操作,但并不能保證線程安全.volatile很容易被誤用,用來進(jìn)行原子性操作, public volatile static int volatileCount = 0; static Object obj =new Object(); public static AtomicInteger atomicCount;// 正確的方法1,使用原子操作 static class MyObject{// 正確的方法4,使用地址引用,多線程是通過地址操作。值的改變是同一個變量(地址) static int mycount=0; } public static void inc1() { MyObject.mycount++; } public static void inc() { //這里延遲1毫秒,使得結(jié)果明顯 try { Thread.sleep(1); } catch (InterruptedException e) { } //典型錯誤1:在資源對象加鎖顯然是不對的,實(shí)際上毫無意義 //Lock lock =new ReentrantLock(); //lock.lock(); //synchronized (obj) // 正確的方法2,可重人的同步塊操作。這也是最常用的辦法 { count++; volatileCount++; atomicCount.incrementAndGet(); } //lock.unlock(); } public static void main(String[] args) { //同時啟動100個線程,去進(jìn)行i++計算,看看實(shí)際結(jié)果 atomicCount =new AtomicInteger(0); Lock lock =new ReentrantLock(); // 正確的方法3,可重人鎖 ReentrantLock Thread threads[]=new Thread[100]; for (int i = 0; i < 100; i++) { threads[i]=new Thread(new Runnable() { @Override public void run() { //lock.lock();// 正確的方法3,可重人鎖 ReentrantLock Counter.inc(); //lock.unlock(); inc1(); } }); threads[i].start(); } //保障線程全部結(jié)束 for(int i=0;i<100;i++){ try { threads[i].join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.PRintStackTrace(); } } //如果沒有同步鎖.值有可能不同。 System.out.println("運(yùn)行結(jié)果1:Counter.count=" + Counter.count); //atomicCount值都應(yīng)該是一致的 System.out.println("運(yùn)行結(jié)果2:Counter.atomicCount=" + Counter.atomicCount); //atomicCount值。如果沒有同步鎖.值有可能不同。 System.out.println("運(yùn)行結(jié)果3:Counter.volatileCount=" + Counter.volatileCount); //使用地址引用,多線程是通過地址操作。值的改變是同一個變量(地址)。值都應(yīng)該是一致的 System.out.println("運(yùn)行結(jié)果4:Counter.mycount=" +MyObject.mycount); } }
運(yùn)行之后,結(jié)果可能會這樣
運(yùn)行結(jié)果1:Counter.count=96
運(yùn)行結(jié)果2:Counter.atomicCount=100
運(yùn)行結(jié)果3:Counter.volatileCount=97
運(yùn)行結(jié)果4:Counter.mycount=100
如果在52行和54行取消注釋(或者取消32行的注釋),結(jié)果必然如下:
運(yùn)行結(jié)果1:Counter.count=100
運(yùn)行結(jié)果2:Counter.atomicCount=100
運(yùn)行結(jié)果3:Counter.volatileCount=100
運(yùn)行結(jié)果4:Counter.mycount=100
新聞熱點(diǎn)
疑難解答