在併發編程中,須要處理2個關鍵的問題:線程間如何通訊和線程之間如何同步。編程
線程之間的同通訊機制有2種:共享內存和消息傳遞。多線程
同步:程序中用於控制不一樣線程間操做發生相對順序的機制。併發
Java併發採用的是共享內存的模型,同步是顯示進行的,就是程序必須顯示指定(用synchronized、volatile、final)某個方法或某段代碼須要在線程之間是互斥的。Java 線程之間通訊有Java內存模型(JMM)控制,JMM經過控制主內存與每一個線程的本地內存之間的交互,來爲保證內存可見性。函數
重排序是指編譯器和處理器爲了優化程序的性能而對指令序列進行從新排序的一種手段。重排序不會改變單線程的執行結果,可是能夠會改變多線程的執行結果。性能
若是程序是正確同步的,程序的執行結果具備順序一致性,即,程序的執行結果與其在順序一致性模型中執行的結果是一致的。優化
經常使用的同步原語包括synchronized、volatile、final,下面主要介紹它們的原理spa
Volatile修飾的成員變量在每次被線程訪問時,都強迫從共享內存中重讀該成員變量的值。並且,當成員變量發生變化時,強迫線程將變化值回寫到共享內存。這樣在任什麼時候刻,兩個不一樣的線程老是看到某個成員變量的同一個值。線程
使用建議:在兩個或者更多的線程訪問的成員變量上使用volatile。當要訪問的變量已在synchronized代碼塊中,或者爲常量時,沒必要使用。
因爲使用volatile屏蔽掉了VM中必要的代碼優化,因此在效率上比較低,所以必定在必要時才使用此關鍵字。 對象
先寫出volatile特色:排序
可見性:對於一個volatile變量的讀,總能看到任意線程對這個volatile變量最後的寫入。
原子性:對於任意單個volatile變量的讀寫具備原子性,可是相似於volatile++這種複合操做不具有原子性。
有序性:主要是禁止指令重排序。
適應場景:單個線程寫,多個線程讀。提供一種比鎖更輕量級的通訊機制。在功能上鎖比volatile功能更強大,可提供整個臨界區代碼的執行具備原子性。在執行性能上,volatile更有優點。因此若是想用volatile替代鎖,要注意場景:1)對變量的寫操做不依賴於當前值,2)該變量沒有包含在具備其餘變量的不變式中。其實就是爲了保證操做是原子性的。
鎖是Java併發編程中最重要的同步機制,鎖可讓臨界區互斥,還可讓釋放鎖的線程向獲取同一個鎖的線程發送消息。從內存語義上看:釋放鎖和volatile的寫相同、獲取鎖和volatile的讀相同。都是經過主內存發送消息相互通訊的。
與鎖和volatile相比,對final域的讀寫更像是普通變量的訪問,下面介紹final域的內存語義:
對於final域,編譯器和處理器要遵循兩個重排序規則:
一、在構造函數內對一個final域的寫入,與隨後把這個被構造函數對象的引用賦值給一個引用變量,這兩個操做之間不能重排序;
這樣能夠保證對象引用爲任意線程可見以前,保證對象的final域已經被正確的初始化了,而普通的域沒有這個保障。
二、初次讀一個包含final域的對象的引用,與隨後初次讀這個fina域,這兩個操做不能重排序。
這樣能夠保證在讀一個對象的final域以前,必定會先讀包含這個對象的引用,防止在讀普通的域時,這個域尚未被寫線程寫入。