那位先生

个人站

前世五百次回眸,才能换得今生的一次擦肩而过。


对象的共享

1.可见性

1.可见性的定义

当一个线程修改了对象状态后,其它线程能够看到发生的状态变化。为了确保多个线程之间对内存写入操作的可见性,必须使用同步机制。

2.失效数据

在缺乏同步的过程中可能产生失效数据:当线程A读取线程变量A时,可能有另一个线程已经修改了它,线程A读到的就是过期的数据。

3.非原子的64位操作

最低安全性:在线程没有同步的情况下读取变量时,可能会得到一个失效值,但这个值至少是由之前某个线程设置的值,而不是一个随机值。比如说:非volatile类型的64位数值变量(double和long),JVM允许将64位的读操作或者写操作分解为两个32位的操作,因此可能会出现读或者写高32位和低32位的时候不是同一个值。

4.加锁与可见性

内置锁可以用于确保某个线程以一种可预测的方式来查看另一个线程的执行结果。
加锁的含义不仅仅局限于互斥行为,还包括内存可见性。为了确保所有线程都能看到共享变量的最新值,所有执行读操作或者写操作的线程都必须在同一个锁上同步。

5.volatile变量

volatile变量是一种稍弱的同步机制,用于确保变量的更新操作通知到其它线程,而且声明为volatile变量后它就是一个共享的变量,不会缓存到寄存器或者其它处理器不可见的地方。
从内存的可见性角度来看,写入volatile变量相当于退出同步代码块,而读取volatile变量相当于进入同步代码块。
注意:

  • 加锁机制既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性。
  • 使用volatile变量的条件:
    • 对变量的写入操作不依赖变量当前的值,或者你能确保只有单个线程更新变量的值;
    • 该变量不会与其它变量一起纳入不变性条件中;
      • 在访问变量时不需要加锁。

2.发布与逸出

1.发布与逸出的定义

发布一个对象:使对象能够在当前作用域之外的代码中使用(即:在其它地方使用这个对象)。被发布的对象要确保对象及内部状态不被发布,如果一定要发布则一定要确保其安全性。
逸出:某个不应该发布的对象被发布会产生逸出。

2.安全对象构造过程

不要在构造过程中使this引用逸出,比如在构造函数中启动一个线程,this引用会被会被新创建的线程共享。如果一定要在构造函数中创建线程,最好不要立刻启动,而是通过一个start或initialize方法来启动。

3.线程封闭volatile变量

1.线程封闭的定义

当访问共享的可变数据时,通常需要使用同步。如果仅在单线程访问数据,就不需要使用同步,这种技术就是线程封闭。
当某个对象封闭在一个线程时将自动实现线程安全性。
当决定使用线程封闭技术时,通常是因为要将某个特定的子系统实现为一个单线程子系统。

2.Ad-hoc线程封闭

Ad-hoc线程封闭是指维护线程封闭的职责完全由程序实现来承担。Ad-hoc线程封闭很脆弱,不建议使用,只有在特定情况下才使用。

3.栈封闭

堆栈封闭其实就是方法中定义局部变量。不存在并发问题。多个线程访问一个方法的时候,方法中的局部变量都会被拷贝一份到线程的栈中(Java内存模型),所以局部变量是不会被多个线程所共享的。

4.ThreadLocal类

是一种更好的线程封闭的方法。ThreadLocal内部维护了一个map,map的key是每个线程的名称,而map的value就是我们要封闭的对象。ThreadLocal提供了get、set、remove方法,每个操作都是基于当前线程的,所以它是线程安全的。
ThreadLocal对象通常用于防止对可变的变量实例或全局变量进行共享。当某个频繁执行的操作需要一个临时对象,同时又希望避免在每次执行时都需要重新分配该临时对象,就可以使用这项技术。

4.不变性

1.不可变对象

如果某个对象被创建后其状态就不能修改,那么这个对象就称为不可变对象。不可变对象一定是线程安全的。
不可变对象的条件:对象创建后其状态就不能被修改;对象的所有域都是final类型;对象时正确创建的(创建的时候this引用没有逸出)。

2.final域

对于在访问和更新多个相关变量时出现的竞态条件问题,可以将这些对象全部保存在一个不可变对象中来消除。

5.安全的发布

1.发布不可变对象

任何线程都可以在不需要额外同步的情况下安全的访问不可变对象,即使在发布这些对象的时候没有使用同步。

2.发布可变对象的常用模式

发布和使用这些可变对象时都必须使用同步。要安全的发布一个对象,对象的引用以及对象的状态都必须同时对其它线程可见。有以下几种发布方式:

  • 在静态初始化函数中初始化一个对象引用;
  • 将对象的引用保存到volatile类型的域或者AtomicReference对象中;
  • 将对象的引用保存到某个正确构造对象的final类型域中;
  • 将对象的引用保存到一个由锁保护的域中(比如:线程安全的容器:HashTable、synchronizedMap、ConcurrentMap、vector、CopyOnArrayList、synchronizedList、BlockingQueue等)。

3.事实不可变对象

如果对象从技术上来说是可变的,但是其状态发布后就不会发生变化,就称该对象为事实不可变对象。在没有额外的同步情况下,任何线程都可以安全的使用被安全发布的事实不可变对象。

6.总结

  • 不可变对象可以通过任何机制来发布;
  • 事实不可变对象必须通过安全方式来发布;
  • 可变对象必须通过安全方式来发布,并且必须是线程安全的或者由某个锁保护起来。

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
你说多少就多少

比五毛钱特效专业哦