/** * 可见性问题致使,程序运行结果不正确 * 有可能因为编译器,处理器及运行时作一些重排序 */ 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.保护对象。被保护对象只能经过持有特定锁来访问。保护对象包括封装在其余线程安全对象中的对象,以及已发布的而且由某个特定锁保护的对象。
不吝指正。