发布对象
使一个对象能够被当前范围之外的代码所使用,将创建的对象保存到容器中,也可能通过某个方法返回 对象的引用,或者将引用传递到其他类的方法中 • 1 • 2
对象逸出
一种错误的发布,当一个对象还没有构造完成时,就使它被其他线程所见
1、发布的对象只需要被它需要的线程被看见
2、避免对象逸出
发布错误对象:
import java.util.Arrays; //线程不安全的 //发布对象 public class Student { private String[] student = {"张三","李四","王五"}; public String[] getStudent(){ return student; } public static void main(String[] args) { Student unsarePublish = new Student(); System.out.println(Arrays.toString(unsarePublish.getStudent())); unsarePublish.getStudent()[1]="赵柳"; System.out.println(Arrays.toString(unsarePublish.getStudent())); } }
返回结果:
[张三, 李四, 王五] [张三, 赵柳, 王五]
上述例子中我们可以看到,李四已经被赵柳替代,通过public 类的访问级别,发布了这些域,在外部都可以访问这些域,这样的发布对象其实是不安全的,因为无法假设其他线程会不会修改这个域,所以会导致student的值是不确定的,因此是线程不安全的。
如何来进行安全的发布对象呢:
看了网上好多博客,这里也整理了一下,我们就来使用最经典的,单例模式来设计
1 懒汉模式设计
1.1 懒汉模式
/** * 懒汉模式 * 线程不安全 */ public class LazyMode1 { private LazyMode1(){//私有的构造函数} private static LazyMode1 instance = null;//单例对象 //在单线程下是没有问题的 public static LazyMode1 getInstance(){ if(instance == null){ instance = new LazyMode1(); } return instance; } }
这是一个比较正常的单例模式的,是一个线程不安全的类,那么如何让他变成一个线程安全的类呢,看下面一个例子
1.2 synchronized
/** * 懒汉模式 * 线程安全 */ public class LazyMode2 { private LazyMode2(){//私有的构造函数} private static LazyMode2 instance = null; //单例对象 public synchronized static LazyMode2 getInstance(){ if(instance == null){ instance = new LazyMode2(); } return instance; }
1.3 双重同步锁
/** * 懒汉模式 * 但是这个类并不是线程安全的类 */ public class LazyMode3 { private LazyMode3(){//私有的构造函数} private static LazyMode3 instance = null; //单例对象 public static LazyMode3 getInstance(){ if(instance == null){ synchronized(LazyMode3.class){ if(instance == null){ instance = new LazyMode3(); } } } return instance; } }
这个案例,我们使用了双重同步锁的单例模式,但是他并不是一个线程安全的类,因为在JVM和cpu优化,发生了指令重排,在单线程下,是没有影响的,但是在多线程下,就会打乱分配的内存空间和初始化对象的顺序,就会导致我们的结果和预期的不一致,虽然这个发生的概率很小,但是会发生,所以他是线程不安全的类,那么如何能够让他成为一个线程安全的类呢,看下面的例子。