【Java并发编程】安全发布对象

安全发布对象

一、对象发布

在介绍安全发布对象之前,应该首先聊一聊什么是发布对象。发布对象是“使一个对象能够被当前范围之外的代码所使用“

比如是下面三种方式

1.将一个指向该对象的引用保存到其他代码可以访问的地方

public class Student(){
    private String name;
    private int age;
}

public class Record{
    Student s;
}

2.在某一个非私有的方法中返回该引用

public class Student(){
    private String name;
    private int age;

    public Student getStudent(){
        return new Student();
    }
}

3.将引用传递到其他类的方法

//情况三
public class  Student(){
    private String name;
    private int age;
}

public class Record{
    private void getStudentInfo(Student s){
        .....
    }
}

但是发布对象,可能会牵扯到不应该发布的对象被发布的情况,这种现象称为对象逸出。

二、对象逸出

常见的对象逸出有

1.内部的可变状态逸出

import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;

@Slf4j
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()));

    }
}

数组states本身是类的私有的变量,但是以public公有的方式发布出去,导致states已经逸出了它所在的作用域,任何调用者都能修改这个数组的内容。这在多线程环境下是危险的。

2.隐式地使this引用逸出

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Escape {

    private int thisCanBeEscape = 1;

    public Escape() {
        new InnerClass();
    }

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

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

运行结果:
在这里插入图片描述

这个类的内部类的实例里面隐含了对外部类的引用。这样在外部类对象没有完全正确构造之前,就会被发布,有可能有不安全的因素在里面,this引用有逸出的风险,比如说thisCanBeEscape=1 还没执行,这个时候的值就是0 。解决的话可以采用工厂方法 止this引用在构造函数过程中逸出 或者私有构造函数完成对象创建或者监听器的注册等。

三、安全发布的方法

可变对象必须通过安全的方式来发布,这通常意味着在发布和使用该对象的线程时都必须使用同步。

要安全地发布一个对象,对象的引用以及对象的状态必须对其他线程可见。一个正确构造的对象可以通过以下方式来安全地发布:

  1. 在静态初始化函数中初始化一个对象引用
     private staits  Student s=  new Student();
  1. 将对象的引用保存到 volatile类型域或者 AtomicReference对象中
  2. 将对象的引用保存到某个正确构造对象的final类型域中
  3. 将对象的引用保存到一个由锁保护的域中

1和2、4的说法可以参考相关的单例模式,这有之前写的文章【Java设计模式】单例模式,这个写的是比较全面的。

《Java并发编程实战》这本书在这部分的内容,提到在线程安全容器内(如Vector或者synchronizedList等)的同步意味着,将对象放入某个容器,线程安全库的容器类提供了很多的安全发布保证。类库中的其他数据传递机制(例如Future和Exchanger)同样能实现安全发布。

最后需要说明一下:本篇文章参考了《Java并发编程实战》,含有部分内容的引用。

在这里插入图片描述
这本书不适合入门,这篇文章能帮助你读懂安全发布对象这一小节了。

发布了127 篇原创文章 · 获赞 3078 · 访问量 36万+

猜你喜欢

转载自blog.csdn.net/qq_42322103/article/details/102770991