安全发布对象 发布与逸出

对象发布

  • 使一个对象能够被当前范围之外的代码使用;

对象逸出

  • 一种错误的发布 ,当一个对象还没有构造完成时,就使它被其他线程所见;

安全发布对象的4种方式

  • 在静态初始化函数中初始化一个对象引用;
  • 将对象的引用保存到volatile类型域或者AtomicReference对象中;
  • 将对象的引用保存到某个正确构造对象的final类型域中;
  • 将对象引用保存到一个由锁保护的域中;

不安全发布示例

  • UnsafePublish对象的私有域states通过公有getter方法被发布,使其可以被任何线程修改,各线程获取到的state的值是不确定的,这样的发布对象就是线程不安全的;
import com.example.concurrency.annotations.NotThreadSafe;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;

@Slf4j
@NotThreadSafe
public class UnsafePublish {

    private String[] states = {"a", "b", "c"};

    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()));
    }

}

对象逸出示例

  • 在Escape对象还没有构建完成时,其构造器中另开的线程将其this发布出去;
  • 如果要在构造器中创建另一个线程,不要启动它,通过专有的start()或初始化方法同一启动线程;可以使用工厂方法和私有构造器完成对象创建和监听器注册;
  • 创建可变对象时的目标是:在其构建尚未完成时,不能将其发布;
不正确的发布可变对象将导致2种错误
  • 发布线程以外的任何线程都可以看到被发布对象的过期的值;
  • 发布线程以外的任何线程看到的引用是最新的,然而被发布对象的状态却是过期的;

结论:如果一个对象是可变对象,其必须被安全发布;

import com.example.concurrency.annotations.NotRecommend;
import com.example.concurrency.annotations.NotThreadSafe;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@NotThreadSafe
@NotRecommend
public class Escape {

    private int thisCanBeEscape = 0;

    public Escape () {
        new InnerClass();
    }

    private class InnerClass {

        public InnerClass() {
            log.info("{}", Escape.this.thisCanBeEscape);
        }
    }

    public static void main(String[] args) {
        new Escape();
    }
    
}

输出:

11:45:00.443 [main] INFO com.example.concurrency.example.publish.Escape - 0

最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容