原文:慕课网高并发实战(五)- 安全发布对象html
发布对象:使一个对象可以被当前范围以外的代码所使用
对象溢出:一种错误的发布,当一个对象尚未构造完成时,就使它被其余线程所见java
不正确的发布可变对象致使的两种错误:安全
一、发布线程意外的全部线程均可以看到被发布对象的过时的值
二、线程看到的被发布对象的引用是最新的,然而被发布对象的状态倒是过时的并发
下面使用代码对不安全的发布和对象溢出进行说明:
不安全的发布示例函数
import com.gwf.concurrency.annoations.NotThreadSafe; import lombok.extern.slf4j.Slf4j; import java.util.Arrays; /** * 不安全的发布 * @author gaowenfeng * @date 2018-03-19 */ @Slf4j @NotThreadSafe public class UnsafePublish { private String[] states = {"a","b","c"}; /** * 经过public发布级别发布了类的域,在类的外部,任何线程均可以访问这个域 * 这样是不安全的,由于咱们没法检查其余线程是否会修改这个域致使了错误 * @return */ public String[] getStates() { return states; } public static void main(String[] args) { UnsafePublish unsafePublish = new UnsafePublish(); log.info("{}", Arrays.toString(unsafePublish.getStates())); unsafePublish.getStates()[0] = "d"; log.info("{}", Arrays.toString(unsafePublish.getStates())); } }
对象溢出示例高并发
/** * 对象溢出 * 在对象构造完成以前,不能够将其发布 * @author gaowenfeng * @date */ @Slf4j @NotThreadSafe @NotRecommend public class Escape { private int thisCannBeEscape = 0; public Escape(){ new InnerClass(); } /** * 包含了对封装实例的隐藏和引用,这样在对象没有被正确构造完成以前就会被发布,由此致使不安全的因素在里面 * 致使this引用在构造期间溢出的错误,他是在构造函数构造过程当中启动了一个线程,形成this引用的溢出 * 新线程只是在对象构造完毕以前就已经看到他了,因此若是要在构造函数中建立线程,那么不要启动它, * 而是应该才用一个专有的start,或是其余的方式统一启动线程 * 使用工厂方法和私有构造函数来完成对象建立和监听器的注册来避免不正确的发布 */ private class InnerClass{ public InnerClass(){ log.info("{}",Escape.this.thisCannBeEscape); } } public static void main(String[] args) { new Escape(); } }
安全发布对象有如下四种方法:
一、在静态初始化函数中初始化一个对象引用
二、将对象的引用保存到volatile类型域或者AtomicReference对象中
三、将对象的引用保存到某个正确构造的final类型域中
四、将对象的引用保存到一个由锁保护的域中this
1和2实例参考双重检测机制的线程安全的单例模式、4的实例能够参考线程安全的懒汉式单例模式。线程安全的单例模式的几种实现方式参考: 单例模式线程