不可變對象可以在沒有額外同步的情況下亏较,安全地用于任意線程遏佣;甚至發(fā)布它們時亦不需要同步。
安全發(fā)布的模式:
如果一個對象是可變的讯壶,它就必須被安全地發(fā)布料仗,通常發(fā)布線程與消費(fèi)線程都必須同步化。如何確保消費(fèi)線程能夠看到處于發(fā)布當(dāng)時的對象狀態(tài)伏蚊,我們要解決對象發(fā)布后對其修改的可見性問題立轧。
為了安全地發(fā)布對象,對象的引用以及對象的狀態(tài)必須同時對其他線程可見丙挽。一個正確創(chuàng)建的對象可以通過下列條件安全地發(fā)布:
- 通過靜態(tài)初始化器初始化對象的引用肺孵;
- 將它的引用存儲到 volatile域或 AtomicReference;
- 將它的引用存儲到正確創(chuàng)建的對象的final域中颜阐;
- 或者將它的引用存儲到由鎖正確保護(hù)的域中平窘。
線程安全庫中的容器提供了如下的線程安全保證:
- 置入Hashtable、synchronizedMap凳怨、ConcurrentMap 中的主鍵以及健值瑰艘,會安全地發(fā)布到可以從Map獲得它們的任意線程中,無論是直接獲得還是通過迭代器(iterator)獲得:
- 置入vector肤舞、CopyOnWriteArrayList紫新、CopyOnWriteArraySet、synchronizedList或者synchronizedSet中的元素李剖,會安全地發(fā)布到可以從容器中獲得它的任意線程中芒率。
- 置入BlockingQueue 或者 ConcurrentLinkedQueue 的元素,會安全地發(fā)布到可以從隊列中獲得它的任意線程中篙顺。
//靜態(tài)初始化器示例:
public static Holder holder = new Holder(42);
發(fā)布對象的必要條件依賴于對象的可變性:
- 不可變對象可以通過任意機(jī)制發(fā)布偶芍;
- 高效不可變對象必須要安全發(fā)布充择;
- 可變對象必須要安全發(fā)布,同時必須要線程安全或者被鎖保護(hù)匪蟀。
安全地共享對象
在并發(fā)程序中椎麦,使用共享對象的一些最有效的策略如下:
- 線程限制:一個線程限制的對象,通過限制在現(xiàn)場中材彪,而被線程獨(dú)占观挎,且只能被占有它的線程修改。
- 共享只讀(share read-only):一個共享的只讀對象段化,在沒有額外同步的情況下嘁捷,可以被多個線程并發(fā)地訪問,但是任何線程都不能修改它显熏。共享只讀對象包括可變對象與高效不可變對象普气。
- 共享線程安全(shared thread-safe):一個線程安全的對象在內(nèi)部進(jìn)行同步,所以其他線程無須額外同步佃延,就可以通過公共接口隨意地訪問它现诀。
- 被守護(hù)的(Guarded):一個被守護(hù)的對象只能通過特定的鎖來訪問。被守護(hù)的對象包括那些被線程安全對象封裝的對象履肃,和已知被特定的鎖保護(hù)起來的已發(fā)布對象仔沿。
將數(shù)據(jù)封裝在對象內(nèi)部,把對數(shù)據(jù)的訪問限制在對象的方法上尺棋,更易確保線程在訪問數(shù)據(jù)時總能獲得正確的 鎖封锉。