/** * 可見性問題致使,程序運行結果不正確 * 有可能因爲編譯器,處理器及運行時作一些重排序 */ public class Novisibility { private static boolean ready; private static int number; private static class ReaderThread extends Thread{ @Override public void run() { while (!ready){ Thread.yield(); //主動讓出cpu, 進入就緒隊列 } System.out.println(number); } } public static void main(String[] args) { new ReaderThread().start(); number = 42; ready = true; } }
又如: java
/** * get操做可能與最近set值不一致,產生數據失效 */ @NotThreadSafe public class MutableInteger { private int value; public int getValue() { return value; } public void setValue(int value) { this.value = value; } }可作以下修改:
/** * 將get, set同步化,可防止數據失效 */ @ThreadSafe public class MutableInteger { private int value; public synchronized int getValue() { return value; } public synchronized void setValue(int value) { this.value = value; } }
比較典型的用法: 數據庫
private volatile boolean asleep; ... while (!asleep){ // do sth. }
1. 對變量的寫入操做不依賴變量的當前值,或者能確保只有一個線程更新變量的值; 緩存
2. 該變量不會與其餘狀態變量一塊兒歸入不變性條件; 安全
3. 訪問該變量不須要加鎖。 多線程
//對共有靜態變量的發佈,集合內部的變量也會被髮布 public static Set<Object> publishedObject; public void init(){ publishedObject = new HashSet<>(); }
//經過共有方法發佈 private Object publishedObject; public Object get(){ return publishedObject; }
/** * 經過發佈類的內部實例 * this引用被逸出 */ public class ThisEscape { public ThisEscape(EventSource source){ source.registerListener(new EventListener() { @Override public void onEvent(Event e) { // ThisEscape.this實例逸出,但此時該實例並無構造完成 } }); } }
可經過工廠方法避免this逸出: 併發
/** * 經過工廠方法防止this逸出 */ public class SafeListener { private final EventListener listener; private SafeListener(){ listener = new EventListener() { @Override public void onEvent(Event e) { //do sth. } }; } public static SafeListener newInstance(EventSource source){ SafeListener safe = new SafeListener(); source.registerListener(safe.listener); return safe; } }
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>(){ @Override protected Connection initialValue() { try { return DriverManager.getConnection("DB_URL"); } catch (SQLException e) { e.printStackTrace(); } return null; } }; public static Connection getConnection(){ //不一樣線程每次獲得的Connection, 都是獨立的備份 return connectionHolder.get(); }
1.對象建立後其狀態不能修改; jvm
2.對象的全部域都是final類型; ide
3.對象是正確建立的(在對象建立期間,this未逸出)。 函數
/** * 不可變類: * 全部域都是final */ public class OneValueCache { private final BigInteger lastNumber; private final BigInteger[] lastFactors; public OneValueCache(BigInteger lastNumber, BigInteger[] lastFactors) { this.lastNumber = lastNumber; this.lastFactors = lastFactors; } public BigInteger[] getFactors(BigInteger i){ if (lastNumber == null || ! lastNumber.equals(i)){ return null; } else{ return Arrays.copyOf(lastFactors, lastFactors.length); } } }
/** * 使用Volatile類型發佈不可變對象 */ @ThreadSafe public class VolatileCachedFactorizer implements Servlet { private volatile OneValueCache cache = new OneValueCache(null, null); //volatile保證每次寫後最新值對其餘線程可見 @Override public void service(ServletRequest req, ServletResponse repo) { BigInteger i = extractFromRequest(req); BigInteger[] factors = cache.getFactors(i); if (factors == null) { factors = factor(i); cache = new OneValueCache(i, factors); } reponseTo(i, factors); } }
/** * 多線程訪問下,有可能出錯,問題不在Holder自己,而在於未正確地發佈,可將n聲明爲final,避免不正確發佈 */ public class Holder { private int n; public Holder(int n){ this.n = n; } public void assertSanity(){ if (n != n){ throw new AssertionError(""); } } }
1.在靜態初始化函數中初始化一個對象引用; 性能
2.將對象的引用保存到volatile類型地域或AtomicReference對象中;
3.將對象的引用保存到某個正確構造對象地final類型域中;
4.將對象的引用保存到一個由鎖保護的域中。
1.不可變對象能夠經過任意機制來發布;
2.事實不可變對象必須經過安全方式來發布;
3.可變對象必須經過安全方式來發布,而且必須是線程安全的或由某個鎖保護起來。
1.線程封閉。線程封閉的對象只能由一個線程擁有,對象被封閉在該線程中,只容許這個線程修改;
2.只讀共享。在沒有同步的狀況下,共享的只讀對象能夠由多個線程併發訪問,但任何線程不能修改它。共享的只讀對象包括不可變對象和事實不可變對象。
3.線程安全共享。線程安全的對象在其內部實現同步,多個線程能夠經過公有接口對其訪問而不需進一步同步;
4.保護對象。被保護對象只能經過持有特定鎖來訪問。保護對象包括封裝在其餘線程安全對象中的對象,以及已發佈的而且由某個特定鎖保護的對象。
不吝指正。