UP | HOME

对象分享

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 无法保证操作的原子性 , 只能 保证变量的可见性

  1. 更改 不依赖于当前值 , 或者能够确保 只会在单一线程中修改变量的值
  2. 变量 不需要其他状态变量 共同参与 不变约束
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;
        }  
    }  
  1. 使用一个 private的构造器启动线程注册监听
  2. 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);
        }
    }
    

     

安全发布 

  1. 线程限制 :如果限制对象只可由单一的线程访问, 那么无论公开哪个成员, 都 不会产生并发问题
  2. 公开不可变成员 :如果对象的某个成员是不可变的, 那么公开该成员 不会产生并发问题
  3. 公开事实上的不可变成员 :如果对象的某个成员是可变的, 但 约定访问该成员的所有线程不要去修改这个成员 , 那么该成员是事实上不可变的. 这种场景下公开该成员 不会产生并发问题
  4. 公开线程安全的成员 :线程安全的成员 内部会妥善并发 问题, 因此公开线程安全的成员是恰当的
  5. 公开可变的非线程安全的成员 :这就要求所有访问该成员的线程使用 特定的锁 进行同步

Next:对象组合

Previous:线程安全

Up:目录