併發編程藝術筆記

減少上下文切換   (Lmbench3 時長、vmstat 次數)
  1.無鎖併發   任務分段
  2.CAS
  3.使用最少線程     任務少,線程多,大多線程處於等待狀態
  4.協程   單線程實現多任務
  
避免死鎖常見方法:
    1.避免一個線程同時獲取多個鎖。
    2.避免一個線程內在鎖內佔用多個資源,儘量保證每個鎖佔用一個
    3.嘗試使用定時鎖lock.tryLock
    4.對於數據庫鎖,加鎖解鎖必須在一個連接內
    
資源限制(將串行執行的部分變成併發執行,資源不足會變慢)
    硬件:帶寬,硬盤,使用集羣  數據id%機器數
    軟件:數據庫連接數,socket數   使用資源複用
   根據不同的資源限制調整程序的併發度
   
synchronized實現同步的基礎:
    1.普通同步方法,鎖實例對象
    2.靜態            Class對象
    3.同步方法塊,鎖括號中對象 synchronized(對象)
    在jvm中使用Monitor對象來實現,方法同步(也可用以下方法實現這裏用另外
    的一種方法)、
    代碼塊同步(monitorenter,monitorexit)

線程通信:1.共享內存(隱式通信)  2.消息傳遞(之間發送消息,顯式)
            顯式同步,需要指定互斥塊     隱式同步發送必須在接受消息前

java使用共享內存模型

在沒有  數據依賴性      的情況下,會進行指令重排
      (讀寫、寫讀、寫寫)

   當存在控制相關性的時候  也會進行指令重排
   if(flag){
      a = i*i;
   }
   先執行temp=i*i;(存入重排序緩衝)  然後判斷flag,爲真執行a=temp;
 單線程中對控制依賴的操作重排序不會改變執行結果,多線程可能會!!

順序一致性內存模型
    1.一個線程所有操作必須按照程序的順序進行
    2.所有線程都能看到單一操作執行順序,每個操作原子,且立刻對其他線程可見。

JMM(java memory model)通過同步程序來達到順序一致性效果,儘可能爲編譯器和處理器優化便利。

JMM 不保證
    1.單線程內操作按程序順序執行
    2.所有線程都能看到一致的操作執行順序
    3.對64bit的long double 寫操作的原子性 jdk5以後讀爲原子操作
                         

volatile的內存語義
        (可見性)
            對被該修飾的變量,單個讀和寫可看做是使用同一個鎖對單個讀寫進行同步
        如果是多個volatile操作或者複合操作(volatile++)整體上不具有原子性
        (單個volatile變量的讀寫的原子性)
            讀,,寫,,寫讀,讀寫不具有原子性
        (部分有序性)
    操作1 普通讀寫   操作2 volatile寫禁止,,其他的允許
        當第二個操作是volatile寫的時候,禁止重排序
        當第一個操作是volatile讀,禁止重排序
        第一位volatile寫,第二爲volatile讀寫不允許排序     普通讀寫可以

-------------------------------------------------------------------------------------------
普通讀寫                                                volatile讀
StoreStore屏障  禁止普通寫與下一步重排                 LoadLoad屏障    禁止後面的
volatile寫                                               LoadStore屏障   讀寫和volatile重排
StoreLoad屏障 禁止上一步與後面可能的volatile讀寫重排     普通讀寫
----------------------------------------------------------------------------------------------
LoadLoad      確保Load1數據裝載先於Load2以及所有後續裝載指令
LoadStore     確保Load1數據裝載先於Store一局所有後續存儲指令
StoreStore      確保Store1數據對其他處理器可見(刷新到內存)先於Store2以及所有後續存儲指令
StoreLoad      確保Store1數據對其他處理器可見(刷新到內存)先於Load2以及所有後續裝載指令的裝載。


鎖的內存語義    除了讓 臨界區互斥執行  釋放鎖的線程向獲取同一個鎖的線程發送消息
    1.線程A釋放一個鎖,實質上是A向接下來獲取該鎖的某線程發送消息
    2.線程B獲取一個鎖,實質上是B獲取了之前的某個線程發出的消息
    3。A釋放,B獲取,即A通過主內存向B發送消息

AQS(AbstractQueueSynchronizer)


final域的重排序規則
以下不能重排序
    1.構造函數內對一個final域的寫入,與隨後把該對象的引用賦值給一個引用變量
    2.初次讀一個final域的對象的引用,與隨後初次讀這個final域   
 final域的寫重排序規則
    1.JMM禁止編譯器把final域的寫重排到構造函數之外
    2.編譯器會在final寫之後,構造函數return之前,插入一個StoreStore屏障禁止把final寫重排到構造函數之
   外
 final域的讀重排序規則
    1.一個線程中初次讀對象引用與初次讀對該對象包含的final域,JMM禁止處理器重排序這兩個操作(僅對CPU)
  會在final域讀操作之前插入一個LoadLoad  兩個操作之間存在間接依賴關係,禁止少數CPU重排序這兩個操作。

DoubleCheck和延遲初始化    減少創建類的開銷增加了耗時
    DoubleCheck
        利用volatile                         禁止 初始化對象與設置指向instance的空間重排序
        
    基於類初始化                               允許重排序,但是不被其他線程看到
        private static class InstanceHolder{
            public static Resource re = new Resource();    //會使用 Class對象初始化鎖 和 state字段
        }                                                 //其中狀態有noInitialization  initializing
        public static Resource getResource(){            //initialized   三個狀態 ,其他檢測到字段wei
            return InstanceHolder.re;                 //第二個,就釋放鎖,掛起等待喚醒
        }
  
Daemon線程 在start之前設置  其線程中的finally塊不一定執行


第4章
Thread.interrupted()對線程的中斷方法進行復位

安全線程終止應使用 中斷   或者    標識位

   !!!僅僅通過flag     然後循環等待,會 難以保持及時性,難以 降低開銷

通過一個object的wait(會釋放鎖)  notify  ,以及flag  循環等待  來對兩個線程之間傳遞消息
ThreadA ThreadB  需要都獲得 object的對象鎖
    notifyAll() 會把所有等待線程全移動到同步隊列  被移動的線程狀態由Waiting變爲Blocked
    
    static boolean flag = true;
    static Object lock = new Object();
ThreadA    
    synchronized(lock){
        while(flag){
        lock.wait();
        }
        處理邏輯
    }
ThreadB
   synchronized(lock){
        flag = false;
        lock.notifyAll();
    }

thread.join()  join()會在thread死亡後返回當前線程

ThreadLocal 可以用於計算方法時間   set(obj)   get(obj)  以一個ThreadLocal爲鍵,任意對象爲值的存儲結構


線程等待超時模式 
    public synchronized Object get(long mills) throws InterruptedException{   //對當前對象加鎖
        long future = System.currentTimeMills()+mills;  //   等待終止時間
        long remaining = mills;                         //   剩餘等待時間
        while((result==null)&&remaining>0){             //   沒有得到結果 且 仍存在等待時間 循環等待
            wait(remaining);                            //等待
            remaining = future - System.currentTimeMills();   //  等待後 剩餘時間
        }
        return result;   //返回結果
    }
    


  

    
    

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章