什麼叫線程安全安全
一個類是線程安全的是指多個線程同時訪問該類時,該類都表現出正確的行爲。併發
線程安全的因素:原子性,可見性。原子性是指一系列操做要麼不執行,要麼所有執行。破壞原子性的操做主要存在兩個地方:競態條件,複合操做。競態條件,是指某個操做的結果的正確性依賴於線程的執行順序。爲了保證原子性,必須採用加鎖機制。可見性,可見性是指一個線程修改某個變量時,其餘線程能看到變量的變化,若是沒有同步將沒法實現這一點。可見性沒有知足時會帶來失效數據(過期的數據或者錯誤的數據),可見性的保證經過加鎖和輕量的volatile變量來實現,volatile變量知足變量的寫操做必須在讀操做以前的happen_before順序,經過內存屏障實現。app
前面講述的是如何保證一個對象內部域的線程安全性,一個對象被共享時如何保證它的線程安全呢?固然一個對象能不共享最好不共享了,能夠經過線程封閉的方法來實現,常見的TreadLocal。若是非共享不可,首先要保證該對象是安全發佈的,針對不變對象怎麼發佈都行,針對事實不變對象須要安全發佈,針對可變對象須要安全發佈,且必須有鎖保護起來。安全發佈一個對象能夠經過靜態初始化函數初始化一個引用,volatile變量聲明或者AtomicRefrence變量聲明,存放到線程安全容器中,將對象的引用保存到一個鎖保護的域中。提醒一點,安全的共享對象最基本的前提是該對象引用沒有逃逸。在實際編碼過程當中,設計一個線程安全類時可能有時候不須要考慮這麼多,能夠將線程安全性委託給現有的線程安全基礎模塊,好比同步容器類,併發容器類,阻塞隊列,或者經過使用同步工具類來實現一個線程安全類。函數
同步容器類:HashTable,Vector,Sychronizedxxx。這些類實現線程安全的方法,是對每一個狀態變量的訪問都進行加鎖,這種的實現方式的弊端是每次都只能有一個線程訪問該容器,其他線程將阻塞,且不保證複合操做(先判斷在檢查,迭代)的同步,必須得客戶端額外加鎖(加的鎖必須是該容器的對象鎖)。其中迭代器的是非線程安全的,當迭代過程又線程修改容器時迭代器將爆出ConcurrentModifyException。工具
併發容器:concurrentHashmap,copyOnWriteArrayList,BlockQueue。ui
同步工具類:CountdownLatch,semophere,FutureTask,CyclicBarrier。CountdownLatch提供兩個方法await,countDown,初始化時閉鎖維護一個計數器,運行時線程將阻塞在await方法上,直到閉鎖的計數器值爲0,其中閉鎖的計數器的狀態由countDown方法遞減,計數器的值不可重置。通俗的來說就是線程在等待一個事件的發生,當該事件發生時各個線程就各幹各的。semaphore提供兩個方法acquire(),release()方法,初始化時信號量維護一個計數器,運行時線程將阻塞在acquire方法上,直到信號量的值爲0,執行線程任務,release()信號量,它跟閉鎖的區別是技術器可重複使用。CycliBarrier的語意是線程將阻塞,直到全部的線程都到達柵欄位置,線程釋放,重置柵欄。CyclicBarrier只有一個await方法,線程將阻塞在該方法上直到全部的線程都到達柵欄,若其中一個線程阻塞在await方法上,可是阻塞超時或者阻塞中斷,柵欄將打破,並拋出異常。編碼