【设计模式】单例模式

1.单例模式有以下特点:

(1)单例类只能有一个实例。

(2)单例类必须自己创建自己的唯一实例。

(3)单例类必须给所有其他对象提供这一实例。

单例模式的作用就是保证在整个应用程序的生命周期中,任何一个时刻,单例类的实例都只存在一个。

任何一个单例模式至少要满足以下的三大点:

(1)必须有一个私有的静态变量指向本类本身。

(2)必须有一个私有、参数为空的构造器。

(3)必须有一个公有、静态方法、返回自身实例。

2.懒汉式单例

package SingletonMode;

public class Singleton {
    private static Singleton singleton;
    private Singleton()
    {}
    public static Singleton getInstance()
    {
        if(singleton==null)
        {
            singleton=new Singleton();
        }
        return singleton;
    }
    
}
package SingletonMode;

public class Client1 {

    public static void main(String[] args) {
        Singleton c1 = Singleton.getInstance();
        Singleton c2 = Singleton.getInstance();        
        System.out.println(c1);
        System.out.println(c1);
        System.out.println(c1==c2);        
    }

}

运行结果:

从以上结果可知,虽然创建了2个不同的实例,但是实际上访问的是同一个实例。由于构造函数被设置为 private ,所以无法再在 Singleton 类的外部使用 new 来实例化一个实例,只能通过访问 getInstance()来访问 Singleton 类。

      在多线程环境下,我们就要考虑到线程的安全问题:即是否有不同的线程分别new一个对象实例,导致不同线程创建的class类的实例不是同一个。

第一种方法:在方法加上同步synchronized

package SingletonMode;

public class Singleton {
    private static Singleton singleton;
    private Singleton()
    {}
    public static synchronized Singleton getInstance()
    {
        if(singleton==null)
        {
            singleton=new Singleton();
        }
        return singleton;
    }
    
}

但synchronize锁是要消耗资源的,所以直接用synchronize锁锁住getInstance方法的话,每个线程来的时候都会额外造成synchronize锁带来的资源消耗问题。

第二种方法:双重检测锁

package SingletonMode;

public class Singleton {
    private static Singleton singleton;
    private Singleton()
    {}
    public static  Singleton getInstance()
    {
        if(singleton==null)
        {
            synchronized(Singleton.class)
            {
                if(singleton==null)
                {
                    singleton=new Singleton();
                }
            }
            
        }
        return singleton;
    }
    
}

不把synchronize锁加到getInstance方法上面,而是加到singleton==null这个if判断的代码块上面,然后再在里面加上singleton==null的判断。

第一个if判断,是为了节省synchronize锁带来的额外资源消耗,保证一旦singleton已经被实例化,后面的线程就不用再new 类的实例了。

而第二个if判断,则是为了解决线程安全问题,因为可能会有多个线程同时运行到singleton==null这行代码,这就会造成多个线程会继续执行下去,执行singleton=new Singleton();

这还是会产生多个不同的Singleton的singleton实例。但是多加一个singleton == null判断,即使有多个线程同时运行到synchronize锁这个部分,但是这个代码块只能有一个线程进入,

所以第一个线程进入后,singleton被实例化,其他线程之后进入后,也无法执行singleton=new Singleton();这行代码。(原文链接:https://blog.csdn.net/sinat_41712356/article/details/117128051)

第三种方法:静态内部类

package SingletonMode;

public class Singleton {
    private static class LazyHolder{
        private static final Singleton singleton=new Singleton();
    }

    private Singleton()
    {}
    public static final Singleton getInstance()
    {
        return LazyHolder.singleton;
    }
    
}

方法三比方法一、方法二更好,减少同步的性能影响。

      静态内部类是在外部类调用时才加载的,也就是说静态内部类可以在外部类加载之后,再加载到jvm中。所以我们在静态内部类中设置一个静态变量,然后指定初始值。因为我们之前提到的类在加载过程中,静态变量只会被初始化一次,所以在jvm层面中,保证了线程的安全性。因为静态内部类被调用之后才会加载初始化,所以就避免了资源浪费问题。(原文链接:https://blog.csdn.net/sinat_41712356/article/details/117128051)

3.饿汉式单例

 饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。

package SingletonMode;
public class Singleton1 {
    private static Singleton1 singleton = new Singleton1();
    private Singleton1()
    {}
    public static Singleton1 getInstance()
    {
        return singleton;
    }
}

4.饿汉式和懒汉式区别

  创建形式   线程安全
懒汉式单例 使用时才创建实例      否(使用同步、双重检查锁定、静态内部类实现线程安全)
饿汉式单例 事先创建实例      是

 

参考文章:

https://www.cnblogs.com/BoyXiao/archive/2010/05/07/1729376.html

https://blog.csdn.net/jason0539/article/details/23297037

 https://blog.csdn.net/sinat_41712356/article/details/117128051

posted @ 2023-03-09 10:57  YorkShare  阅读(184)  评论(0编辑  收藏  举报