一、内存可见性
在没有同步的情况下,编译器、处理器以及运行时等都可能对操作的执行顺序进行一些意想不到的调整。在缺乏足够同步的多线程程序中,要想对内存操作的执行顺序进行判断,几乎无法得出正确的结论。
使用volatile修饰的变量,最低安全性适合于绝大多数的变量,非volatile类型的64位数值变量(long,double)。
加锁的行为不仅是实现互斥还包括内存可见性。为了确保所有的线程都能够看到共享变量的最新值,所有执行读操作和写操作的线程都必须在同一个锁上同步。
volatile变量:(访问不加锁)
作用:用来确保变量的更新操作通知到其他线程。
当变量声明为volatile类型后,编译器与运行时都会注意到这个变量时共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volitale变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此读取volitale类型的变量时总会返回最新写入的值。
volatile变量的通常用法:做为某个操作完成、发生中断或者状态的标志。
满足一下条件才可以使用volitale:
1、对变量的写操作不依赖于变量的当前值,或者你能确保只有单个线程更新变量的值
2、该变量不会与其他状态变量一起纳入不可变条件中
3、在访问变量时不需要加锁。
二、发布和逸出
三、线程封闭
一种避免同步的方式就是不共享数据。如果仅在单线程内访问数据,就不需要同步。局部变量和ThreadLocal类,栈封闭其实就是局部变量,一个方法在运行的时候开启一个栈,将变量封装在栈中。ThreadLocal对象常用于防止对可变的单实例变量或者全局变量进行共享。
四、不变性
不可变对象一定是线程安全的
不可变对象的满足条件:
1、对象创建以后其状态不能修改
2、对象的所有域都是final类型
3、对象是正确创建的(在创建期间,this引用没有逸出)
正如“除非需要更高的可见性,否则应将所有域都声明为私有域”,同样,“除非需要某个域是可变的,否者应将其声明为final域”
使用volitale类型来发布不可变对象实现线程的安全
五、安全发布
常用的安全发布对象的模式:安全地发布一个对象,对象的引用以及对象的状态必须同时对其他线程可见。
1、在静态初始化函数中初始化一个对象的引用;(例如单例模式的懒汉模式)
2、将对象的引用保存到volatile类型或者atomicreference对象中
3、将对象的引用保存到某个正确构造对象的final类型域中;
4、将对象的引用保存到一个由锁保护的域中;(例如安全容器)
对象的发布需求取决于它的可变性
1、不可变对象可以通过任意机制来发布;
2、事实不可变对象必须通过安全方式来发布;
3、可变对象必须通过安全方式来发布,并且必须是线程安全的或者由某个锁来保护起来;
并发线程使用共享对象的一些实用策略:
1、线程封闭
2、只读共享(不可变对象和事实不可变对象)
3、线程安全共享(???)
4、保护对象(锁机制)