单例模式饿汉和懒汉

单例分为懒汉式和饿汉式:

饿汉式:类加载的时候,创建对象。 因此类加载速度慢, 线程相对安全。
懒汉式:类加载的时候,不会创建对象,调用时才会创建对象。因此类加载速度快,线程相对不安全,一般要配合synchronized使用。

饿汉式:

//饿汉式
public class HungarySingleton {
    private static HungarySingleton single = new HungarySingleton();

    private HungarySingleton() {
        System.out.println("---new HungarySingleton---");
    }

    public static HungarySingleton getSingle() {
        return single;
    }
}

 

懒汉式:

//懒汉式
public class LazySingleton {
    private static LazySingleton single = null;

    private LazySingleton() {
        System.out.println("---new LazySingleton---");
    }

    // 懒汉模式在使用时,容易引起不同步问题,所以应该创建同步锁
    // 双重锁写法。在获取对象的时候,先判断是否为空,不为空直接返回,否则就加锁,加完锁然后再次需要判断是否为空。
    public static LazySingleton getSingle() {
        if (single == null) {
            synchronized (LazySingleton.class) {
                if (single == null) {
                    single = new LazySingleton();
                }
            }
        }
        return single;
    }

}

 

线程安全性分析:

测试类:

public class Test {
    public static void main(String[] args) {
        for (int i = 0; i < 50000; i++) {
            new Thread() {
                public void run() {
                    HungarySingleton.getSingle();
                }
            }.start();

        }
    }
}

 

 

 

 不管是饿汉式还是懒汉式,都只有一个new,说明都是线程安全的。

 

为什么懒汉式需要用双重锁写法?

我们先注释掉里面的非空判断,再运行测试类。

 

运行结果:

如果把里面的判null去掉,再去测试。发现有两个输出,说明不是单例。

为什么哪,因为可能出现这种情况,线程A、B同时请求加锁,此时两个线程都已通过了第一个为空的判断,假设A线程先加锁,B线程等待,然后A去创建对象;A释放锁后,B线程加锁,又创建了对象。

 

单例模式思路:

1.私有属性
2.私有构造方法
3.公开的getSingle方法

单例模式作用:保证了对象的唯一性

适用场景:
1.需要生成唯一序列的环境
2.需要频繁实例化然后销毁的对象。
3.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
4.方便资源相互通信的环境

 

最后咱们来谈下,单例模式的优缺点:

优点:

1.实现了对唯一实例访问的可控
2.对于一些需要频繁创建和销毁的对象来说可以提高系统的性能。

缺点:

1.不适用于变化频繁的对象
2.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出。
3.如果实例化的对象长时间不被利用,系统会认为该对象是垃圾而被回收,这可能会导致对象状态的丢失。

posted @ 2019-11-28 17:18  gaopengpy  阅读(205)  评论(0)    收藏  举报