对象分享
Table of Contents
可见性
过时的变量
一个线程 修改了变量的值 , 另一个线程 并非 总是能够 及时获知最新的值
public class NoVisibility { private static boolean ready; private static int number; private static class ReaderThread extends Thread { public void run() { while (!ready) Thread.yield(); System.out.println(number); } } public static void main(String[] args) { new ReaderThread().start(); number = 42; //reader线程无法看见 ready = true; } }
- 64位变量 可能 非原子
- 加锁 可以保证 可见
volatile变量
volatile 无法保证操作的原子性 , 只能 保证变量的可见性 :
- 更改 不依赖于当前值 , 或者能够确保 只会在单一线程中修改变量的值
- 变量 不需要 与 其他状态变量 共同参与 不变约束
volatile boolean asleep; ... while (!asleep) countSomeSheep();
this逃逸
在 构造函数返回之前 其他线程 就持有该对象的引用
public class ThisEscape { private int a = 1; public ThisEscape() { new Thread(new EscapeRunnable()).start(); this.a = 2; } private class EscapeRunnable implements Runnable { @Override public void run() { // 通过ThisEscape.this就可以引用外围类对象, 但是此时外围类对象可能还没有构造完成, 即发生了外围类的this引用的逃逸 this.a += 1; } }
- 使用一个 private的构造器 中 启动线程 或 注册监听
- public的工厂方法 来调用构造器
public class ThisEscape { private final Thread t; private ThisEscape() { // private 的构造器 t = new Thread(new EscapeRunnable()); // 启动线程 // ... } public static ThisEscape getInstance() { // public的工厂方法来调用构造器 ThisEscape escape = new ThisEscape(); escape.t.start(); return escape; } private class EscapeRunnable implements Runnable { @Override public void run() { // 此时可以保证外围类对象已经构造完成 } } }
线程限制
栈限制
变量申明在方法内
ThreadLocal类
线程不安全的JDBC Connection可以放在 ThreadLocal 里面
不变对象
构造完成后 所有属性无法改变 ,所有属性都是 final
不会出现this逃逸
volatile和不变对象进行同步
- 当 对象状态发生变化 的时候, 重新构造一个新的不变对象
用 volatile 保证每个 新构造的不变对象 可见性
@Immutable class OneValueCache { private final BigInteger lastNumber; private final BigInteger[] lastFactors; public OneValueCache(BigInteger i, BigInteger[] factors) { lastNumber = i; lastFactors = Arrays.copyOf(factors, factors.length); } public BigInteger[] getFactors(BigInteger i) { if (lastNumber == null || !lastNumber.equals(i)) return null; else return Arrays.copyOf(lastFactors, lastFactors.length); } } @ThreadSafe public class VolatileCachedFactorizer implements Servlet { private volatile OneValueCache cache = new OneValueCache(null, null); public void service(ServletRequest req, ServletResponse resp) { BigInteger i = extractFromRequest(req); BigInteger[] factors = cache.getFactors(i); if (factors == null) { factors = factor(i); cache = new OneValueCache(i, factors); } encodeIntoResponse(resp, factors); } }
安全发布
- 线程限制 :如果限制对象只可由单一的线程访问, 那么无论公开哪个成员, 都 不会产生并发问题
- 公开不可变成员 :如果对象的某个成员是不可变的, 那么公开该成员 不会产生并发问题
- 公开事实上的不可变成员 :如果对象的某个成员是可变的, 但 约定访问该成员的所有线程不要去修改这个成员 , 那么该成员是事实上不可变的. 这种场景下公开该成员 不会产生并发问题
- 公开线程安全的成员 :线程安全的成员 内部会妥善并发 问题, 因此公开线程安全的成员是恰当的
- 公开可变的非线程安全的成员 :这就要求所有访问该成员的线程使用 特定的锁 进行同步