二 Java多線程-線程關鍵字

Java中和線程相關的關鍵字就兩:volatile和synchronized 。
volatile以前用得較少,以后會用得更少(后面解釋) 。它是一種非常輕量級的同步機制 , 它的三大特性是:
1、保證可見性,即強制將CPU高速緩存的數據立即寫入主存,會導致其他CPU核中對應的高速緩存內容無效,就像這樣:

二 Java多線程-線程關鍵字

文章插圖
如果由于同步需要,某行代碼使用了volatile關鍵字,那么當CPU內核1收到指令時 , 會立即將它位于高速緩存中的數據(這里是字符串“1”)寫到主存中去,那么其余的數據(“哈哈”、“test”、9.9)會全部失效 。
2、有序性(禁止指令重排)
所謂指令重排,就是對于int a = 0、 int b = 1這類賦值語句,編譯成class字節碼時,a = 0,b = 1的順序可能會因為編譯器的優化而導致和書寫時的順序不一致 。但如果有c = a + b , 那么這行代碼必須在前兩句的后面執行 。這個不用深究,知道就好了 。
3、不保證原子性
所謂不保證原子性,就是如果遇到多個線程同時執行i = i + 1,那么i可能就不能保證僅僅被加1了 。
根據Java的內存模型整理出如下這些規則,看看就好,能理解就記?。?理解不了也沒關系 , 不用記:
二 Java多線程-線程關鍵字

文章插圖
下面還是以代碼來說明:
public class VolatileTest extends Thread {static boolean flag = true;@Overridepublic void run() {while (flag) {}System.out.println("子線程結束");}public static void main(String[] args) throws InterruptedException {new Thread(new VolatileTest()).start();TimeUnit.SECONDS.sleep(1);flag = false;System.out.println("主線程結束");}}加上volatile之后 , 子線程就不會一直卡住了:
public class VolatileTest2 extends Thread {volatile static boolean flag = true;@Overridepublic void run() {while (flag) {}System.out.println("子線程結束");}public static void main(String[] args) throws InterruptedException {new Thread(new VolatileTest2()).start();TimeUnit.SECONDS.sleep(1);flag = false;System.out.println("主線程結束");}}其實在第一個類VolatileTest中,即使不用volatile關鍵字,也能讓程序正常結束,只需要添加一行代碼就行了:
public class VolatileTest3 extends Thread {static boolean flag = true;@Overridepublic void run() {while (flag) {// 只要在這里隨便加一行代碼,雖然原理不同,但效果和加了volatile一樣System.out.println(" ");}System.out.println("子線程結束");}public static void main(String[] args) throws InterruptedException {new Thread(new VolatileTest3()).start();TimeUnit.SECONDS.sleep(1);flag = false;System.out.println("主線程結束");}}總結一下,適應volatile的條件:
1、對變量的寫操作不依賴于當前值 , 就是不會出現 x++ 或 x = x + y 這樣的自增性質的語句;
2、變量沒有包含在具有其他變量的代碼中,就是不會出現 if(x > y) { // dosomething; } 這樣的語句,因為這樣的話,dosomething就不是線程安全的 。
所以適合使用volatile的場景是:
1、狀態標記量
2、雙重檢查
狀態標記量:只設置簡單的狀態值,希望立即看到 , 且無其他變量參與 。之前Volatile2中的flag就是這樣的狀態標記量 。
雙重檢查(這個有點生僻冷門,不深究) 。
因為目前synchronized的性能已經有了大幅提升,而且機器硬件的性能也較之前翻了幾十倍,volatile存在的意義已經不大了 。所以如果對volatile的理解不夠深入,就干脆不用理解了 。
再來看另一個重量級選手:synchronized
synchronized關鍵字用于解決多個線程之間的資源共享問題 , 通常有三大作用域:
1、靜態方法:public synchronized static void methodName() , 進入方法前要獲得當前類對象的鎖;
2、實例方法:public synchronized void methodName(),進入方法前要獲得當前對象實例的鎖;
3、代碼塊:最常用,指定一個特別的加鎖對象,進入同步代碼塊前要獲得這個對象鎖 。
這幾個就不一個個地說了,因為網上這類例子太多了 。
就簡單說一下volatile和synchronized的比較:
1、volatile是synchronized的輕量級實現,性能要比synchronized要好;
2、volatile只能修飾變量,synchronized只能修飾方法和代碼塊;
3、volatile不會造成阻塞,synchronized會 。
最后再說一個我之前遇到的小問題:
如果使用的IDE是Eclipse,那么當前活動的線程數量可能是1;

推薦閱讀