三维分析Java单例模式各实现方式的优缺点

一、饿汉式(Eager Singleton)

package main.singleton;

/**
 * Created by leboop on 2018/11/27.
 * 声明为final,不可被继承
 */
public final class EagerSingleton {
    //定义实例对象时直接初始化
    private static EagerSingleton instance = new EagerSingleton();

    //构造方法私有化,不允许外部new
    private EagerSingleton() {
    }

    private static EagerSingleton getInstance() {
        return instance;
    }
}

1、final修饰类

final修饰的类不允许被其他类继承。

2、构造方法私有化

这一步是保证单例的基础,如果构造方法没有私有化,外部可以通过new的方式创建多个实例。

3、static修饰instance成员变量和getInstance成员方法

首先单例的外部不可以通过构造方法创建单例对象,只能通过EagerSingleton.getInstance的方式获取对象,所以getInstance成员方法需要声明为static,而static方法只能访问static成员变量,所以instance也需要使用static修饰。

4、维度分析

(1)线程安全

instance作为类变量在初始化的过程中会被收集进<clinit>()方法中,该方法能够保证同步,所以instance在多线程的情况下只能被实例化一次。

(2)性能

如果EagerSingleton中成员变量比较少,且占用的内存资源不多,该方式也未尝不可;但是,如果EagerSingleton中成员变量占用资源较多,那么该方式会有些不妥。instance被ClassLoader加载后可能很长时间才被使用,意味着instance实例开辟的堆内存空间会驻留更久的时间。

(3)懒加载

该方式无法进行懒加载。

二、懒汉式(Lazy Singleton)

package main.singleton;

/**
 * Created by leboop on 2018/11/27.
 * final修饰:不可被其他类继承
 */
public final class LazySingleton {
    //构造方法私有化:不允许外部new实例化
    private LazySingleton() {
    }
    //定义实例,不直接初始化
    private static LazySingleton singleton = null;

    //方法中实例化
    public static LazySingleton getInstance() {
        if (singleton == null) {
            singleton = new LazySingleton();
        }
        return singleton;
    }
}

1、final修饰类

同上。

2、构造方法私有化

同上。

3、static修饰instance成员变量和getInstance成员方法

同上。

4、维度分析

(1)线程安全

该方式是线程不安全。类变量instance=null,因此当LazySingleton.class被初始化的时候instance并不会被实例化,会在getInstance中判断是否为空后再决定是否需要实例化。现在我们假设有两个线程:线程1和线程2,当线程1调用getInstance()方法时,看到instance==null,未被实例化,那么线程1将会执行该方法中的instance=new LazySingleton(),但是假设线程1未执行实例化前,线程2也调用了getInstance方法,读到instance=null,也会进入if语句执行instance=new LazySingleton(),此次会创建两个instance实例,每个线程拥有一个。我们测试一下:

package main.singleton;

import java.util.concurrent.TimeUnit;

/**
 * Created by leboop on 2018/11/27.
 * final修饰:不可被其他类继承
 */
public final class LazySingleton {
    //构造方法私有化:不允许外部new实例化
    private LazySingleton() {
    }

    //定义实例,不直接初始化
    private static LazySingleton singleton = null;

    //方法中实例化
    public static LazySingleton getInstance() {
        if (singleton == null) {
            System.out.println(Thread.currentThread().getName());
            singleton = new LazySingleton();
        }
        return singleton;
    }
}
package main.singleton;


import java.util.HashSet;
import java.util.Set;

/**
 * Created by leboop on 2018/11/27.
 */
public class SingletonTest {
    private static Set<LazySingleton> set = new HashSet<>();

    public static void main(String[] args) {
        new Thread(() ->
        {
            LazySingleton singleton=LazySingleton.getInstance();
            synchronized (set) {
                set.add(singleton);
            }
        }

        ).start();

        new Thread(() ->
        {
            LazySingleton singleton=LazySingleton.getInstance();
            synchronized (set) {
                set.add(singleton);
            }
        }

        ).start();

        while (Thread.activeCount() > 2) {

        }
        System.out.println("set的大小:" + set.size());
        if (set.size() > 1) {
            for(LazySingleton singleton:set){
                System.out.println(singleton);
            }
        }
    }
}

运行结果:

Thread-0
Thread-1
set的大小:2
main.singleton.LazySingleton@a3aa81
main.singleton.LazySingleton@1b2e63

 (2)性能

性能比饿汉式高。不能保证单例,性能已经无从谈起。

(3)懒加载

该方式是懒加载。

三、懒汉式(Lazy Singleton)+synchronized

package main.singleton;

/**
 * Created by leboop on 2018/11/27.
 */
public final class SynchronizedLazySingleton {
    //构造方法私有化:不允许外部new实例化
    private SynchronizedLazySingleton() {
    }

    //定义实例,不直接初始化
    private static SynchronizedLazySingleton singleton = null;

    //方法中实例化
    public static synchronized SynchronizedLazySingleton getInstance() {
        if (singleton == null) {
            singleton = new SynchronizedLazySingleton();
        }
        return singleton;
    }
}

我们直接在懒汉式的getInstance方法上添加了同步关键字synchronized。

维度分析:

(1)线程安全

该方式因添加了synchronized关键字保证了线程安全

(2)懒加载

该方式是懒加载

(2)性能

synchronized关键字天地的排他性导致了getInstance方法只能在同时时候被一个线程访问,性能低下。

四、改进懒汉式(Lazy Singleton)+synchronized

package main.singleton;

/**
 * Created by leboop on 2018/11/27.
 */
public final class SynchronizedLazySingleton {
    //构造方法私有化:不允许外部new实例化
    private SynchronizedLazySingleton() {
    }

    //定义实例,不直接初始化
    private static SynchronizedLazySingleton singleton = null;

    //方法中实例化
    public static SynchronizedLazySingleton getInstance() {
        if (singleton == null) {
            synchronized (SynchronizedLazySingleton.class) {
                if (singleton == null) {
                    singleton = new SynchronizedLazySingleton();
                }
            }
        }
        
        return singleton;
    }
}

刚方式使用同步代码块改进了第三种方式:懒汉式(Lazy Singleton)+synchronized。当两个线程发现singleton ==null,只要一个线程可以进入同步代码块,完成对instance的实例化,等线程1同步代码块执行完毕,线程2进入同步代码块,发现singleton已经实例化,无需进行任何操作。以后对getInstance()方法的访问就不需要进入同步代码块,较第三种方式性能有提高。

五、Holder方式

package main.singleton;

/**
 * Created by leboop on 2018/11/27.
 */
public final class HolderSingleton {
    
    private HolderSingleton(){}
    
    private static class Holder{
        private static HolderSingleton holderSingleton=new HolderSingleton();
    }
    
    private static HolderSingleton getInstance(){
        return Holder.holderSingleton;
    }
}

该方式是一个线程安全,性能高,懒加载的方式,也是目前使用比较广的设计之一。

六、枚举方式

package main.singleton;

/**
 * Created by leboop on 2018/11/27.
 */
public enum EnumSingleton {
    SINGLETON;

    EnumSingleton() {
        System.out.println("SINGLETON将被初始化");
    }

    public static void method() {
        System.out.println("method的方法被调用");
    }

    public static EnumSingleton getInstance() {
        return SINGLETON;
    }
}

三维分析:

(1)线程安全

该方式是线程安全的。

(2)性能

性能比较只是相对的,推荐使用该方式。

(3)懒加载

无法懒加载

注:当调用method()方法时,会主动初始化。

七、Holder+枚举方式

package main.singleton;

/**
 * Created by leboop on 2018/11/27.
 */
public class HolderEnumSingleton {
    //构造方法私有化
    private HolderEnumSingleton() {
    }
    //使用enum枚举充当Holder
    private enum EnumHolder {
        INSTANCE;
        private HolderEnumSingleton instance;

        EnumHolder() {
            this.instance = new HolderEnumSingleton();
        }

        private HolderEnumSingleton getSingleton() {
            return instance;
        }
    }
    //获取单例
    public static HolderEnumSingleton getInstance() {
        return EnumHolder.INSTANCE.getSingleton();
    }
}

三维分析:

(1)线程安全

该方式是线程安全的

(2)性能

推荐使用。

(3)懒加载

该方式是懒加载的。

参见《Java高并发编程详解》

猜你喜欢

转载自blog.csdn.net/L_15156024189/article/details/84560586