多个线程称操作不同实例对象。多个线程要操作同一个对象,要保证对象的唯一性。
实例过程中只实例化一次。
public class Test {
//首次类加载进行初始化
private static Test test = new Test();
//实例化私有化
private Test() {
}
public static Test getInstance() {
return test;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Test.getInstance().hashCode());
}).start();
}
}
}
说明
因为static关键字,在类加载的时候已经被实例化,所有只有一个实例,是线程安全的。
缺点
没有延迟加载 长时间不使用,占用空间
public class Test1 {
private static Test1 instance = null;
private Test1() {
}
public static Test1 getInstance() {
if (instance == null) {
instance = new Test1();
}
return instance;
}
}
说明
if判断为空,才进行加载。
缺点
不能保证线程安全,不能保证实例对象的唯一性。
A线程进入new Test(),还没return对象时,CPU时间分片结束,B线程执行,进入if判断,判断为null,B线程就会进行new Test(),进行实例化,此时返回,A线程也进行返回,此时就会产生两个对象。
解决线程安全需要加synchronized
public static synchronized Test1 getInstance() {
if (instance == null) {
instance = new Test1();
}
return instance;
}
改进后,性能不佳
public class DCL {
private static DCL dcl = null;
private static DCL getInstance() {
if (dcl == null) {
synchronized (DCL.class) {
if (dcl == null) {
dcl = new DCL();
}
}
}
return dcl;
}
}
说明
两个if,双重检查
性能比较好,保证了线程的安全性
缺点
可能会发生空指针异常
java实例化对象不是一个原子性操作,线程A在执行new DCL()时,当完成一部分,dcl已经非空,但是此时实例对象并没有构造完成,线程B进来判断if 就会得到dcl非空,直接返回,从而导致空指针
缺点说明
java创建一个对象,在虚拟机上会进行三个步骤
这里会产生重排序问题,1 2 3的执行顺序是随机的,其中3执行完后,对象就不为null了,所以3在2前执行,当3执行完2还没执行,其他线程进来就会返回执行3的对象,从而导致空指针。
解决重排序方式
volatile+DCL
private static DCL dcl = null;
声明类的时候,不声明实例变量,而放到静态类中进行初始化。
public class Test2 {
private Test2(){
}
//静态内部类
private static class Holder {
private static Test2 test2 = new Test2();
}
public static Test2 getInstance() {
return Holder.test2;
}
}
说明
这种方式使用了类的加载方式,在类加载时不会进行初始化,只有去调用getInstance方法时,才会去进行初始化,并返回实例,这种方式实现了懒加载,没有加锁,效率更高。
这个得应用比较广泛。
枚举是可以看作一种常量,只能实例化一次,在加载的时候进行实例化。
//这个不能保证懒加载
public enum Test3 {
INSTANCE;
public static Test3 getInstance() {
return INSTANCE;
}
}
public class EnumSingletonDemo {
private EnumSingletonDemo() {
}
//延迟加载
private enum EnumHolder {
INSTANCE;
private EnumSingletonDemo instance;
EnumHolder() {
this.instance = new EnumSingletonDemo();
}
}//懒加载
public static EnumSingletonDemo getInstance() {
return EnumHolder.INSTANCE.instance;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
System.out.println(EnumSingletonDemo.getInstance().hashCode());
}).start();
}
}
}
原文:https://www.cnblogs.com/wilsonsui/p/12804434.html