单例模式----懒汉式在多线程下存在线程安全问题

设计模式

设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
设计模式是一种针对特定问题的解决方案,所以应该是很多的。在面向对象的语言中,主流、常用的有23种设计模式。

单例模式

在面向对象的语言中,一个类,可以创建多个对象。但是,在有的场景下,只能要求有一个该类的对象,不能创建多个(比如连接池),在这种情况下,就会用到单例模式。

单例模式的一般思想就是:在本类中,定义一个该类的成员变量,然后手动创建一个私有的构造方法(可以阻止自带无参构造方法,私有化之后也不能在外部创建该类的对象),然后暴露一个方法,可以获取这个唯一的实例。
根据创建对象时机不同,可分为饿汉式和懒汉式,由于懒汉式存在线程安全问题,所以又可以分为用synchronized和未使用synchronized。

饿汉式:
//饿汉式
public class Single1
{
    //定义并初始化一个静态的成员变量,并且使用final修饰(这里不用final修饰也可以的,私有的无法从外部改变)
    private static final Single1 single  = new Single1();

    //将构造方法私有化,不能从外部创建对象
    private Single1() { }

    //暴露一个公开的静态方法,可以获取这个唯一的实例
    public static Single1 getInstance()
    { return single; }
}

饿汉式在类被加载进内存的时候,就将这个唯一的实例创建出来了,所以不存在线程安全问题。但是有个缺点,如果没有用到这个实例,只要这个类被加载进了内存,就一定会将这个实例创建出来,所以可能会浪费内存

懒汉式----未使用synchronized
//懒汉式---未使用synchronized关键字,
public class Single2
{
    //定义一个静态的成员变量,但并不初始化
    private static  Single2 single;

    //将构造方法私有化,不能从外部创建对象
    private Single2() { }

    //暴露一个公开的静态方法,可以获取这个唯一的对象
    public static Single2 getInstance()
    {
        //第一次调用这个方法的时候,就将这个对象创建出来。没有调用过就不创建
        if (single == null)
        { single = new Single2(); }
        return single;
    }
}

懒汉式可以解决饿汉式那个浪费内存的问题,但是另一个问题就是:在多线程环境下,可能会拿到多个该类的实例,从而满足单例的需求。因为多个线程可能同时调用这个方法,同时判断这个对象还没有被创建出来,就各自创建一个实例。不过这个问题可以使用synchronized关键字解决

懒汉式—使用synchronized关键字,
//懒汉式---使用synchronized关键字,
public class Single3
{
    //定义一个静态的成员变量,但并不初始化
    private static Single3 single;

    //将构造方法私有化,不能从外部创建对象
    private Single3() { }

    //暴露一个公开的静态方法,可以获取这个唯一的对象
    //synchronized是重量级锁、排它锁……
    public static synchronized Single3 getInstance()
    {
        //第一次调用这个方法的时候,就将这个对象创建出来。没有调用过就不创建
        if (single == null)
        { single = new Single3(); }
        return single;
    }
}

使用了synchronized关键字,就不会存在线程安全问题了。但是,由于synchronized是排它锁,一个线程在调用这个获取实例的方法的时候其他的线程就不能再调用这个方法了。所以在多线程的场景下,效率应该会低一些。

代码测试

public class Test02
{
    public static void main(String[] args)
    {
        //多线程下,未使用synchronized
        //noSynchronized();

        //多线程下,使用synchronized
        //hasSynchronized();
    }

    //多线程下,未使用synchronized
    public static void noSynchronized()
    {
        for (int i = 0; i < 100; i++)
        {
            new Thread(new Runnable()
            {
                @Override
                public void run()
                { System.out.println("未使用synchronized: "+Single2.getInstance()); }
            }).start();
        }
    }

    //多线程下,使用synchronized
    public static void hasSynchronized()
    {
        for (int i = 0; i < 100; i++)
        {
            new Thread(new Runnable()
            {
                @Override
                public void run()
                { System.out.println("使用synchronized: "+Single3.getInstance()); }
            }).start();
        }
    }
}

饿汉式一定是线程安全的,所以没有测试,只测试了懒汉式未使用synchronized和使用synchronized。

结果

未使用synchronized

未使用synchronized虽然大部分都是同一个实例,但是还是出现了少数的其它实例。

使用synchronized

使用了synchronized
所有的实例都是同一个!

有什么不对或不懂的地方,欢迎讨论交流!

猜你喜欢

转载自blog.csdn.net/ql_7256/article/details/107459183