可见性
一个共享变量被一个线程修改后,另外一个线程能够正确的获取到共享变量最新值,此共享变量具有可见性。
失效数据
缺乏同步的共享变量,在多线程访问中,就可能获取到失效数据,从而导致不可预见的错误。
非原子的64位操作
正常情况下,即使是非同步变量,不同线程获取到的不是最新数据,为失效数据,但至少它是之前存在过的值,这个值曾经出现过,这种就是所谓的最低安全性。jvm将非volatile类型的long和double变量(64位数值)分解为两个32位操作,多个线程修改变量后,可能读取到不是一个值的高32位和低32位。
加锁与可见性
所有执行读操作和写操作的线程都在同一个锁上同步,保证了单个线程在修改某变量时对其他线程可见。加锁机制既可以保证可见性,又可以保证原子性。
Volatile变量
将变量的通知同步到其他线程,但是不要过度依赖此关键字提供的可见性,避免在需要对可见性执行复杂判断的地方使用(依赖变量当前的值),典型用法是检查某个状态标志是否退出循环。
发布与逸出
发布是使一个对象能够在当前作用域之外的代码中使用,逸出指发布了不该发布的对象,说白了就是错误的发布,导致某对象能够在指定的作用域之外的地方使用。
安全的对象构造过程(有点乱)
不要在构造函数中使this逸出,这一点还不好用文字描述,大概来说在对象构造完成之前(构造函数中),调用了this对象的方法,就上书中调用了doSomething()一样,此方法属于成员方法。另外使用匿名内部类就可以了在这个内部类中包含了ThisEscape的引用,this.doSomething(),从而导致this的方法在外部可以访问。
可以参考:https://segmentfault.com/q/1010000007900854/a-1020000018104804
线程封闭
数据共享会有安全问题,那么让数据不进行共享,只能单线程访问,则可以保证数据的安全,将变量封闭在线程内部,就是这个意思。
Ad-hoc线程封闭
维护线程封闭的职责由程序来承担,使用单线程访问共享变量,由于是单线程访问,所以。
栈封闭
局部变量就是被封闭在线程内部的,如果是一个基本数据类型,那么它肯定是线程安全的,如果是引用类型,则需要确保这个引用是被封闭在局部变量中的。
ThreadLocal类
将变量保存在Thread中,封闭在每个线程内部,确保其他线程无法访问。
不变性
对象的状态在确定之后无法修改,成为不可变对象,对象的状态不可变后,当时是线程安全的。要确保对象不可变,有三个必要条件:1、所有的域都使用final修饰2、正确的构造对象,保证this引用没有逸出3、对象创建以后状态就不可改变
Final域
final类型的域是不可修改的。另,变量声明时除非需要更大的作用域,否则都应该声明为私有域,除非变量时可变的对象,否则都应该使用final修饰声明为不可变对象。
使用volatile类型来发布不可变对象
由于是不可变对象,所以在更新状态的时候,需要重新创建不可变对象,这时使用volatile来保证线程能够看到最新的状态。
安全发布
多个线程安全的共享变量
不正确的发布:正确的对象被破坏(暂时理解不透彻)
Holder不是一个不可变对象,它的成员变量没有使用同步,导致多个线程都可以不安全的访问。书上说如果使用fianl修饰Holder中的n就可以解决安全发布的问题,大概意识就是说就变为不可变对象。
不可变对象与初始化安全性
直接上书上的结论吧,任何线程都可以在不需要额外同步的情况下安全地访问不可变对象,即使在发布这些对象时没有使用同步。
安全发布的常用模式(暂时理解不透彻)
对象的引用和对象的状态必须同时对其他线程可见,才是可变对象的安全发布:
使用静态初始化函数初始化一个对象的引用,静态初始化,只会在类加载的时候执行一次,所以。
将对象引用的域保存到volatile域或者AtomicReference对象中。
将对象的引用保存到某个正确构造对象的final类型域中。
将对象引用保存在有锁保护的一个域中。
事实不可变对象
可变对象在构造出来之后,没有改变的需求,形成事实上的不可变对象
可变对象
不仅需要安全的发布,状态的修改也需要保证其的安全性。使用同步机制。
安全的共享对象
发布的对象所具有的正确的访问方式,通过公有接口,或者特定的锁等。