JAVA中的单例模式

作者: 冬天里的懒喵 | 来源:发表于2017-08-19 16:32 被阅读108次

在java中,单例模式的实现方法有如下几种:

1.饿汉模式

所为饿汉模式,即一开始就创建一个静态的对象,之后该对象一直存在。这种模式不会有线程安全问题。

package com.dhb.builder.singleton;

public class Singleton1 {
    
    private static Singleton1 instance = new Singleton1();

    private Singleton1() {
    }
    
    public static Singleton1 getInstance() {
        return Singleton1.instance;
    }

}

2.懒汉模式

对于饿汉模式,优点在于实现简单。但是存在一个问题就是 instance 只要 Singleton1被加载就会被创建到static所在的静态方法区。如实现数据库连接池等情况,用这种方法一上来就要创建数据库的连接资源,实际系统中暂不使用。这就造成了资源的浪费。因此,对于这种情况,出现了与之对应的懒汉模式。
即一开始并不创建对象,待需要使用时再new。

package com.dhb.builder.singleton;

public class SingletonDemo1 {
    
    private static SingletonDemo1 instance = null;
    
    private SingletonDemo1() {
        
    }

    /**
     * 存在线程安全问题
     * @return
     */
    public static SingletonDemo1 getInstance() {
        if(instance == null) {
            instance = new SingletonDemo1();
        }
        return instance;
    }
}

这是大家想到的最常用的懒汉模式的写法。但是问题来了,上述模式在多线程的情况下是线程不安全的!也就是说,如果有两个线程,同时getInstance(),同时都会判断instance的值为null。这种情况下会创建多个实例。
为了解决上述问题,我们引入了锁:

package com.dhb.builder.singleton;

public class SingletonDemo2 {

    private static SingletonDemo2 instance = null;

    private SingletonDemo2() {
        
    }

    /**
     * 增加同步机制,解决线程安全
     * @return
     */
    public static synchronized SingletonDemo2 getInstance() {
        if(instance == null) {
            instance = new SingletonDemo2();
        }
        return instance;
    }
}

上面这种做法,确实解决了线程安全问题,但是带来了一个更加不好的问题,那就是每一次请求都会加锁!这样会严重影响性能。更好的做法是采用双重检查机制:

package com.dhb.builder.singleton;

public class SingletonDemo3 {

    private static SingletonDemo3 instance = null;

    private SingletonDemo3() {
        
    }

    /**
     * 增加双重检查机制,解决synchronized效率问题
     * @return
     */
    public static  SingletonDemo3 getInstance() {
        if(instance == null)
            synchronized (SingletonDemo3.class) {
                if (instance == null) {
                    instance = new SingletonDemo3();
                }
            }
        return instance;
    }
}

上述单例实际仍然存在问题,那就是类初始化仍然需要时间,如果同时又两个线程同时进入getInstance方法,第一个线程锁定之后,第二个线程判断不为空,则直接使用instalce,如果此时第一个线程对SingletonDemo3对象还没实例化完成,如该对象内部存在一个耗时的引用,如果是一个数据库连接,则会导致第二个线程使用的对象不完整。出现空指针。因此更好的写法是加上volatile。以保证happen-before原则。

package com.dhb.builder.singleton;

public class SingletonDemo4 {

    private volatile static SingletonDemo4 instance = null;

    private SingletonDemo4() {
        
    }

    /**
     * 增加双重检查机制,解决synchronized效率问题
     * @return
     */
    public static SingletonDemo4 getInstance() {
        if(instance == null)
            synchronized (SingletonDemo4.class) {
                if (instance == null) {
                    instance = new SingletonDemo4();
                }
            }
        return instance;
    }
}

这样单例模式才完全解决。上述方法比较冗繁,有没有更好的解决办法呢,有幸阅读过《effective java》这本书对于单例有更好的解决办法。

3.更好的解决办法

第一种方法,利用静态内部类:

package com.dhb.builder.singleton;

import java.util.stream.IntStream;

public class SingletonHolder {

    private SingletonHolder() {

    }
    private static class InstanceHolder{
        private final static SingletonHolder INSTANCE = new SingletonHolder();
    }

    public static SingletonHolder getInstance() {
        return InstanceHolder.INSTANCE;
    }

    public static void main(String[] args) {
        IntStream.rangeClosed(1,100).forEach(i -> new Thread(String.valueOf(i)){
            @Override
            public void run() {
                System.out.println(SingletonHolder.getInstance());
            }
        }.start());
    }
}

上述方法执行结果:

com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6

可以看出SingletonHolder类只实例化了一次。这种方法很巧妙地利用一个内部类,很简单的代码即实现了单例,而且是线程安全。

方式二:《effective java》中还有一种更简单的写法,那就是枚举。也是《effective java》作者最为推崇的方法。

package com.dhb.builder.singleton;

import java.util.stream.IntStream;

public class SingletonEnum {

    private SingletonEnum() {

    }

    private enum Singleton {
        INSTANCE;

        private final SingletonEnum instance;

        Singleton() {
            instance = new SingletonEnum();
        }

        public SingletonEnum getInstance() {
            return instance;
        }
    }

    public static SingletonEnum getInstance() {
        return Singleton.INSTANCE.getInstance();
    }

    public static void main(String[] args) {
        IntStream.rangeClosed(1,100).forEach(i -> new Thread(String.valueOf(i)){
            @Override
            public void run() {
                System.out.println(SingletonEnum.getInstance());
            }
        }.start());
    }
}

上述方法执行结果:

com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6

在java中,枚举天然实现了单例模式。其构造方法只会实例化一次。

相关文章

  • Java中单例模式你用的哪一种?

    一起讨论java中的单例模式。单例模式是java设计模式中算是最简单的设计模式了。 * java实现单例模式的写法...

  • Java四种单例设计模式

    Java中的四种单例模式 单例模式是最容易理解的设计模式之一,介绍Java中单例模式的四种写法。 1.基本单例模式...

  • 单例模式

    JAVA设计模式之单例模式 十种常用的设计模式 概念: java中单例模式是一种常见的设计模式,单例模式的写法...

  • Android 架构5 设计模式之单例模式

    前言 Java中单例(Singleton)模式是一种广泛使用的设计模式。单例模式的主要作用是保证在Java程序中,...

  • JAVA多线程之线程安全的单例模式

    概念:java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例、饿汉式单例、登记式单例三种。单例模式有一...

  • Java几种单例模式的实现

    Java中单例(Singleton)模式是一种广泛使用的设计模式。单例模式的主要作用是保证在Java程序中,某个类...

  • Java -- 单例设计模式

    Java中单例(Singleton)模式是一种广泛使用的设计模式。单例模式的主要作用是保证在Java程序中,某个类...

  • java的单例模式——其实很简单

    Java中单例(Singleton)模式是一种广泛使用的设计模式。单例模式的主要作用是保证在Java程序中,某个类...

  • 单例模式学习

    Java中单例(Singleton)模式是一种广泛使用的设计模式。单例模式的主要作用是保证在Java程序中,某个类...

  • 单例模式

    3、单例模式(Singleton) 单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象...

网友评论

    本文标题:JAVA中的单例模式

    本文链接:https://www.haomeiwen.com/subject/qlbtdxtx.html