【java并发编程实战】第二章:对象的共享

1.重要的属性

可见性,不变性,原子性

1.1可见性

当一个线程修改某个对象状态的时候,我们希望其他线程也能看到发生后的变化。

在没有同步的情况下,编译器和处理器会对代码的执行顺序进行重排。以提高效率。重排后的顺序是不可预知的,所以在多线程中无法对执行结果进行判断
看下面的代码

public class NoVisibility {

    private static boolean ready;

    private static int number;

    private static class ReaderThread extends Thread {
        @Override
        public void run() {
            while (!ready) {
                System.out.println(">>>");
            }
            System.out.println(1/number);
        }
    }

    public static void main(String[] args) {
        new ReaderThread().start();

        number = 1;

        ready = true;
    }

}

上面的执行结果可能会一直执行下去,也可能抛错。我们期望number=1以后,ready=true执行,最后输出1/1=1,由于重排,可能导致ready先初始化,然后线程读取到的还是ready=true,number=0.导致异常.


最低安全性:线程在没有同步的情况下可能会得到失效的值,但是至少这个值是之前某个线程设置的,而不是随机值。但有一个例外,看下面
其中提到了一个很有趣的非原子的64位操作
jvm内存模型规定,变量的读写操作都要原子性,但是有个例外,非volatile修饰的long和double这64位的操作,jvm允许将64位的读写操作分成2个32位的操作,就可能导致读取到的某个值的高32位和另外一个值的低32位。
除非用volatile或者锁保护起来


1.1.1 volatile

基础
1.弱同步机制,volatile更新后的值将会通知其他线程。
2.变量修饰后,jvm或处理器不会对其进行指令重排。
3.不会被缓存在寄存器或者其他处理器看不到的地方。所以读取的值都是最新的。
其中与加锁不同的是,加锁可以保证原子性和可见性,但是volatile修饰后只能保证可见性。

1.1.2 threadlocal

threadlocal是为了防止可变的单实例变量或全局变量进行共享。jdbc就是采用了threadlocal。

threadlocal这里只描述场景和并发的关系。需要独立篇幅进行原理分析。

1.1.3

解决并发问题的另一个解决方案,不变形对象。不可变对象一定是线程安全的。

对象不可变的满足条件:

①对象创建后就不可修改
②对象的所有域都是final修饰
③对象是正确创建的,创建期间没有this溢出被其他地方修改。

final修饰的List,Map等都是可以继续添加元素。可以采用Collections.unmodifiableXXX()。其实它的原理不过是实现一个Map接口。重写了put和remove等修改的时候进行异常抛出的操作。

    private static class UnmodifiableMap<K,V> implements Map<K,V>, Serializable {
        private static final long serialVersionUID = -1034234728574286014L;

        private final Map<? extends K, ? extends V> m;

        UnmodifiableMap(Map<? extends K, ? extends V> m) {
            if (m==null)
                throw new NullPointerException();
            this.m = m;
        }

        // 省略

        public V put(K key, V value) {
            throw new UnsupportedOperationException();
        }
        public V remove(Object key) {
            throw new UnsupportedOperationException();
        }
        public void putAll(Map<? extends K, ? extends V> m) {
            throw new UnsupportedOperationException();
        }
    }
posted @ 2018-08-23 16:57  吃海苔的winton  阅读(126)  评论(0编辑  收藏  举报