volatile、synchronize關(guān)鍵字,Lock類的定義與特性

volatile關(guān)鍵字

定義:對該變量禁止使用CPU緩存,而從主內(nèi)存中讀寫
特性:
  1. 禁止編碼優(yōu)化(禁止指令重排序)
  2. 保證變量的線程可見性,即線程B對線程A的操作是可見的,即原則1 遵循h(huán)appens-before原則
  3. 不會對線程阻塞,而只是對變量的"讀或?qū)?保證原子性,但不對"讀并且寫"保證原子性??梢岳斫鉃橛袃蓚€鎖:讀鎖和寫鎖,但不可同時讀和寫,見increase方法;故此時一寫多讀時可以保證數(shù)據(jù)一致。
    若要多寫多讀,synchronize關(guān)鍵字或Lock類
    volatile static int inc = 0;

    public static void main(String[] args) {
        Runnable runnable = ()->{
            increase();
        };
        ExecutorService executorService = Executors.newFixedThreadPool(20);
        for (int i = 0; i < 20; i++) {
            executorService.execute(runnable);
        }
        // 等待所有線程結(jié)束
        executorService.shutdown();
        System.out.println(inc);
    }

    private static /*synchronized*/ void increase() {
        for (int i = 0; i < 1000; i++) {
            // 如線程A執(zhí)行至256次內(nèi)存讀到256,線程B已經(jīng)執(zhí)行完1000次寫入內(nèi)存1000,此時線程A第257次內(nèi)存讀到1000,繼續(xù)剩下的743次循環(huán)
            inc++; // inc = inc + 1 讀inc并且寫inc
        }
    }

19418
Process finished with exit code 0

happens-befores原則:

定義:前一個操作的結(jié)果對后續(xù)操作是可見的

共8條原則
主要為

  1. 順序性
  2. volatile原則
  3. 傳遞性
    ...
    Java并發(fā)編程實戰(zhàn)
    java 8大happen-before原則超全面詳解

synchronize關(guān)鍵字

定義:Java中互斥鎖技術(shù)的實現(xiàn)
特性:
  1. 可修飾方法,代碼塊
class X {
  // 修飾非靜態(tài)方法
  synchronized void foo() {
    // 臨界區(qū)
  }
  // 修飾靜態(tài)方法
  synchronized static void bar() {
    // 臨界區(qū)
  }
  // 修飾代碼塊
  Object obj = new Object();
  void baz() {
    synchronized(obj) {// lock()
      // 臨界區(qū)
    //unlock()
    }
  }
}
  1. 修飾static方法時,實際鎖的是該類的.class對象;修飾非static方法時,鎖的時該this對象
    注意:使用鎖synchronized要注意以哪個對象為鎖,和要保護的資源(臨界區(qū)),在同一個鎖下的臨界區(qū)是保證原子性的
class obj {
  synchronized(obj.class) static void foo1(){}
  // obj.class是單例的
  synchronized(this) void foo2(){}
  // this是可以new出多個的
}
  1. wait()、notify()、notifyAll()只能在sychronized代碼塊中使用
sychronized(this){
  this.wait() // 此處釋放的鎖一定為this,即鎖對象
  // 若鎖對象為targer,則為target.wait()
}

wait():釋放該互斥鎖,同時該線程進入等待隊列,使其他線程可以搶占該鎖 sleep()使線程阻塞,不會釋放鎖
notify(): 隨機通知等待隊列中的一個線程,條件滿足,可以執(zhí)行wait()之后代碼
使用notifyAll():通知隊列中所有線程,條件滿足

Lock接口

定義:Lock為concurrent.locks包下的一個接口,該locks包提供對線程鎖操作的方法。
特性:

1.常用實現(xiàn)類:

        ReentrantLock lock = new ReentrantLock();   // 實現(xiàn)Lock接口
        ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();    // 實現(xiàn)ReadWriteLock,提供讀鎖或?qū)戞i
        readWriteLock.writeLock().lock();  // 讀鎖

2.常用方法:

        lock.lock();// 獲得鎖
        try{
            //處理任務(wù)
        }catch(Exception ex){
        }finally{
            lock.unlock();   // 釋放鎖
        }
        lock.tryLock(); // 嘗試獲得鎖,得到true,得不到false,立即返回

悲觀鎖與樂觀鎖CAS機制

悲觀鎖

定義:總是假設(shè)最壞的情況,假設(shè)每次取數(shù)據(jù)后都認(rèn)為數(shù)據(jù)會被其他線程修改,需要保證數(shù)據(jù)的強一致性。如sychronized關(guān)鍵字和ReentrantLock類,都是悲觀鎖。

樂觀鎖與CAS

定義:假設(shè)每次取得數(shù)據(jù)后,數(shù)據(jù)不會被其他線程修改。
CAS:全稱compareAndSwap,望文生義,即比較與替換。
每次進行對數(shù)據(jù)寫操作時進行一次CAS:
compare:比較工作內(nèi)存中的值A(chǔ)1,是否與主內(nèi)存中地址V中的值A(chǔ)2一致;
swap:若A1與A2一致,則修改地址V中的A2為B;若A1與A2不一致,則重新讀取地址V中的值,再進行任務(wù)處理,稱為回旋,該情況可能一直循環(huán)直到一致為止。

CAS下帶來的ABA問題:
按時間順序有以下任務(wù):
線程1任務(wù):讀地址V,A值修改為B值
線程2任務(wù):讀地址V,A值修改為B值
線程3任務(wù):讀地址V,B值修改為A值
根據(jù)CAS原則,線程1執(zhí)行后,線程2回旋,線程3執(zhí)行,最終值為A
若此時線程2阻塞,則只執(zhí)行1和3,線程2釋放,根據(jù)CAS原則,執(zhí)行完1和3后,線程2回旋判定A值一致,修改為B,最終值為B
解決方法:每次寫數(shù)據(jù)時,加入版本號,判斷A1與A2一致時,同時判斷版本號是否一致
如concurrent包下的Atomic類,為樂觀鎖。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容