首页 > 其他 > 详细

并发编程基础之单例模式

时间:2020-04-29 21:02:25      阅读:65      评论:0      收藏:0      [点我收藏+]

单例模式

多个线程称操作不同实例对象。多个线程要操作同一个对象,要保证对象的唯一性。

实例过程中只实例化一次。

饿汉式

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

改进后,性能不佳

懒汉式+同步方法

DCL 双重检查锁

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、给instance分配内存
  • 2、调用构造方法进行初始化
  • 3、指向内存分配区域

这里会产生重排序问题,1 2 3的执行顺序是随机的,其中3执行完后,对象就不为null了,所以3在2前执行,当3执行完2还没执行,其他线程进来就会返回执行3的对象,从而导致空指针。

解决重排序方式

volatile+DCL

private static DCL dcl = null;

Holder 持有者方式

声明类的时候,不声明实例变量,而放到静态类中进行初始化。

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

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!