谈一下单例模式

单例模式属于创造型设计模式

总的来说可以分为懒汉型和饿汉型单例模式,下面我将列举几种单例模式的写法并逐个分析

1.饿汉式单例模式(线程安全)

package com.wantao.concurrency.singleton;

/**
 * 饿汉式单例模式1
 */
public class Singleton1 {
    private static Singleton1 instance = new Singleton1();//类加载时就创建了对象

    private Singleton1() {
    }

    public static Singleton1 getInstance() {
        return instance;
    }

    public static void main(String[] args) {
        System.out.println(Singleton1.getInstance().hashCode());
        System.out.println(Singleton1.getInstance().hashCode());
    }
}

分析:这是饿汉型单例模式的典型的写法,因为它在类加载时就创建了对象,所以这种方式是属于线程安全的。

2.饿汉型单例模式(线程安全)

package com.wantao.concurrency.singleton;

/**
 * 饿汉式单例模式2
 */
public class Singleton2 {
    private static Singleton2 instance = null;

    static {
        instance = new Singleton2();
    }

    private Singleton2() {

    }

    public static Singleton2 getInstance() {
        return instance;
    }

    public static void main(String[] args) {
        System.out.println(Singleton2.getInstance().hashCode());
        System.out.println(Singleton2.getInstance().hashCode());
    }
}

分析:这也是饿汉式单例模式,与上种写法相比,只是把创建对象的过程放在了static代码块中.但要注意

3.懒汉型单例模式(线程不安全)

package com.wantao.concurrency.singleton;

/**
 * 懒汉式单例模式1
 */
public class Singleton3 {
    private Singleton3() {

    }

    private static Singleton3 instance = null;

    public static Singleton3 getInstance() {
        if (instance == null) {
            instance = new Singleton3();
        }
        return instance;
    }

    public static void main(String[] args) {
        System.out.println(Singleton3.getInstance().hashCode());
        System.out.println(Singleton3.getInstance().hashCode());
    }
}

分析:这是最简单的懒汉式单例模式,它在第一次访问时才创建实例。但这种写法是线程不安全的。

        假设有A,B两个线程同时调用getInstance()方法进行创建,此时instance都为空,两个线程都会new新的对象.

4.懒汉型单例模式(线程安全但是不推荐)

package com.wantao.concurrency.singleton;

/**
 * 懒汉式单例模式2
 */
public class Singleton4 {
    private Singleton4() {

    }

    private static Singleton4 instance = null;

    public static synchronized Singleton4 getInstance() {
        if (instance == null) {
            instance = new Singleton4();
        }
        return instance;
    }

    public static void main(String[] args) {
        System.out.println(Singleton4.getInstance().hashCode());
        System.out.println(Singleton4.getInstance().hashCode());
    }
}

分析:使用synchronized同步方法,线程安全,但是每次只能一个线程获得对象,效率太低.

5.懒汉型单例模式(线程安全但是不推荐)

package com.wantao.concurrency.singleton;

/**
 * 懒汉式单例模式3
 */
public class Singleton5 {
    private Singleton5() {

    }

    private static Singleton5 instance = null;

    public static Singleton5 getInstance() {
        synchronized (Singleton5.class) {
            if (instance == null) {
                instance = new Singleton5();
            }
        }
        return instance;
    }

    public static void main(String[] args) {
        System.out.println(Singleton5.getInstance().hashCode());
        System.out.println(Singleton5.getInstance().hashCode());
    }
}

   分析:这种写法同上,只不过synchronized修饰的不是方法而是代码块.

6.懒汉型单例模式(线程不安全)

package com.wantao.concurrency.singleton;

/**
 * 懒汉单例模式4
 */
public class Singleton6 {
    private Singleton6(){

    }
    private static Singleton6 instance=null;
    public static Singleton6 getInstance(){
        if(instance==null){
            synchronized (Singleton6.class){
                if(instance==null){
                    instance=new Singleton6();
                }
            }
        }
        return instance;
    }
    public static void main(String[] args) {
        System.out.println(Singleton6.getInstance().hashCode());
        System.out.println(Singleton6.getInstance().hashCode());
    }
}

分析:使用双重检测加synchronized(double checked locking)实现单例模式,看上去是线程安全的,但是由于jvm的指令重排,这也是不安全的.

       新建对象一般三个步骤:

                                         1.在堆上面分配了内存空间

                                         2.初始化对象

                                          3.将对象的引用指向刚刚分配的内存空间。

        但经过jvm的指令重排后顺序可能变为了:132

        这样如果有A,B两个线程,当A线程完成了1,3两个步骤时,B线程这时候也获取对象,获取的是未初始化的对象.

7.懒汉型单例模式(线程安全,推荐使用)

package com.wantao.concurrency.singleton;

/**
 * 懒汉单例模式5
 */
public class Singleton7 {
    private Singleton7(){

    }
    private static volatile Singleton7 instance=null;
    public static Singleton7 getInstance(){
        if(instance==null){
            synchronized (Singleton7.class){
                if(instance==null){
                    instance=new Singleton7();
                }
            }
        }
        return instance;
    }
    public static void main(String[] args) {
        System.out.println(Singleton7.getInstance().hashCode());
        System.out.println(Singleton7.getInstance().hashCode());
    }
}

分析:与上面的方法相比,使用了volatile关键字禁止了指令重排,使线程变得安全.

8.懒汉型单例模式(线程安全,推荐使用)

package com.wantao.concurrency.singleton;

/**
 * 懒汉单例模式6
 */
public class Singleton8 {
    private Singleton8(){

    }
    public static Singleton8 getInstance(){
         return Singleton.INSTANCE.getInstance();
    }
    private enum Singleton{
        INSTANCE;
        private Singleton8 instance;
         Singleton(){
            instance=new Singleton8();
        }
        public Singleton8 getInstance(){
             return instance;
        }

    }
    public static void main(String[] args) {
        System.out.println(Singleton8.getInstance().hashCode());
        System.out.println(Singleton8.getInstance().hashCode());
    }
}

分析:使用enum(枚举)的方式,默认就是线层安全的,推荐使用

 

饿汉式与懒汉式单例模式对比:

    1.饿汉式在类加载时就创建了实例,本身就是线程安全的,而懒汉式则在第一次调用时才创建实例,需要加锁或者enum来实现线程安全.

    2.饿汉式单例模式没有加锁所以效率高,但是如果一开始就实例化了但是最后没调用就浪费内存。

 

转载请注明文章地址:https://blog.csdn.net/niangou0915/article/details/90316476

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值