8种方式实现单例模式

请仔细看代码中的注释。

1.饿汉式

package mutithread.concurrent.chapter14;

/**
 * 饿汉式
 * @author zhangjinglong
 * @date 2020-02-19-17:13
 *
 * 如果一个类中的成员属性比较少,且占用的内存资源不多,饿汉的方式未尝不可,相反,如果一个类中的成员都是比较重
 * 的资源,那么这种方式就会有些不妥
 * 饿汉式的单例模式可以保证多个线程下的唯一实例,getInstance方法性能比较高,但是无法进行懒加载
 */

//final不允许被继承
public final class Singleton {

    //实例变量
    private byte[] data=new byte[1024];

    //在定义实例对象的时候直接初始化
    private static Singleton instance=new Singleton();

    //私有构造函数,不允许外部new
    private Singleton(){

    }

    public static Singleton getInstance(){
        return instance;
    }
}

2.懒汉式

package mutithread.concurrent.chapter14;

/**
 * 懒汉式
 * @author zhangjinglong
 * @date 2020-02-19-17:13
 *

 * 懒汉式的单例模式 不能 保证多个线程下的 单例的唯一性
 */

//final不允许被继承
public final class Singleton2 {

    //实例变量
    private byte[] data=new byte[1024];

    //定义实例,但是不直接进行初始化
    private static Singleton2 instance=null;

    //私有构造函数,不允许外部new
    private Singleton2(){

    }

    public static Singleton2 getInstance(){
        //不能保证多线程下的单例唯一性
        if(null==instance)
            instance=new Singleton2();
        return instance;
    }
}

3.懒汉式+同步方法

package mutithread.concurrent.chapter14;

/**
 * 懒汉式+同步方法
 * @author zhangjinglong
 * @date 2020-02-19-17:13
 *

 * 在懒汉式的基础上,增加同步的约束 可以 保证多个线程下的 单例的唯一性
 * 但是synchronized关键字天生但排他性导致了getInstance方法只能在同一时刻被一个线程访问,性能低下
 */

//final不允许被继承
public final class Singleton3 {

    //实例变量
    private byte[] data=new byte[1024];

    //定义实例,但是不直接进行初始化
    private static Singleton3 instance=null;

    //私有构造函数,不允许外部new
    private Singleton3(){

    }

    //向getInstance方法加入同步控制,每次只能有一个线程能够进入
    public static synchronized Singleton3 getInstance(){
        if(null==instance)
            instance=new Singleton3();
        return instance;
    }
}

4.Double-Check

提供了一种高效的数据同步策略

package mutithread.concurrent.chapter14;

import java.net.Socket;
import java.sql.Connection;

/**
 * Double-Check  提供了一种高效的数据同步策略
 * 在首次初始化时加锁,之后则允许多个线程同时进行getInstance方法的调用来获得类的实例
 * @author zhangjinglong
 * @date 2020-02-19-17:13
 *
 * 这种方式既满足了懒加载,又保证了instance实例的唯一性,提高了高效的数据同步策略,
 * 可以允许多个线程同时对getInstance进行访问。
 * 但是,这种方式在多线程的情况下有可能会引起空指针异常。
 * 由于JVM运行时指令重排序和Happens-Before规则,instance,conn和socket三者之间的实例化关系并物前后关系的约束
 * 如instance最先被实例化,而conn和socket并未完成实例化,
 * 未完成实例化的实例调用其方法就会抛出空指针异常
 *
 */

//final不允许被继承
public final class Singleton4 {

    //实例变量
    private byte[] data=new byte[1024];

    //定义实例,但是不直接进行初始化
    private static Singleton4 instance=null;

    Connection conn;

    Socket socket;

    //私有构造函数,不允许外部new
    private Singleton4(){
        this.conn//初始化conn
        this.socket//初始化socket
    }
    public static  Singleton4 getInstance(){
        //当instance 为null 时,进入同步代码块,同时该判断避免了每次都需要进入同步代码块,可以提高效率
        if(null==instance){
            //只有一个线程能够获得Singleton4.class关联的monitor
            synchronized (Singleton4.class){
                //判断如果instance 为null则创建
                if(null==instance){
                    instance=new Singleton4();
                }
            }
        }
        return instance;
    }
}

5.Volatile+Double-Check

为防止JVM在运行时指令重排序导致的问题,采用volatile关键字可以解决

package mutithread.concurrent.chapter14;

import java.net.Socket;
import java.sql.Connection;

/**
 * Volatile+Double-Check
 * @author zhangjinglong
 * @date 2020-02-19-17:13
 *
 *
 *  * 由于JVM运行时指令重排序和Happens-Before规则,instance,conn和socket三者之间的实例化关系并物前后关系的约束
 *  * 如instance最先被实例化,而conn和socket并未完成实例化,
 *  * 未完成实例化的实例调用其方法就会抛出空指针异常
 *  为防止JVM在运行时指令重排序导致的问题,采用volatile关键字可以满足
 *  至此,多线程下的单例,懒加载以及获取实例的高效性都可满足
 */

//final不允许被继承
public final class Singleton5 {

    //实例变量
    private byte[] data=new byte[1024];

    //定义实例,但是不直接进行初始化  注意使用volatile关键字保证conn和socket先实例化
    private volatile  static Singleton5 instance=null;

    Connection conn;

    Socket socket;

    //私有构造函数,不允许外部new
    private Singleton5(){
        this.conn//初始化conn
        this.socket//初始化socket
    }
    public static Singleton5 getInstance(){
        //当instance 为null 时,进入同步代码块,同时该判断避免了每次都需要进入同步代码块,可以提高效率
        if(null==instance){
            //只有一个线程能够获得Singleton4.class关联的monitor
            synchronized (Singleton5.class){
                //判断如果instance 为null则创建
                if(null==instance){
                    instance=new Singleton5();
                }
            }
        }
        return instance;
    }
}

6.Holder方式

这种方式实现简单,原理复杂,仔细品。。

package mutithread.concurrent.chapter14;

import java.net.Socket;
import java.sql.Connection;

/**
 *
 * Holder方式
 * 借助类加载的特点
 * @author zhangjinglong
 * @date 2020-02-19-17:13
 *
 *     //在Single类中并没有instance的静态成员,而是将其放到了静态内部类Holder之中
 *     //因此在Singleton6类的初始化过程中并不会创建Singleton6的实例
 *     //Holder类中定义了Singleton6的静态变量,并且进行了实例化
 *     //当Holer被主动引用的时候则会创建Singleton6的实例
 *     //Singleton6实例的创建过程在JAVA程序编译时期收集至<clinit>()方法中,
 *     //该方法又是同步方法,同步方法可以保证内存的可见性、JVM指令的顺序性和原子性
 *     Holder方式的单例设计是最好的设计之一,也是目前使用比较广的设计之一
 */

//final不允许被继承
public final class Singleton6 {

    //实例变量
    private byte[] data=new byte[1024];

    //私有构造函数,不允许外部new
    private Singleton6(){
    }

    //在静态内部类中持有Singleton6的实例,并且可以被直接初始化
    private static class Holder{
        private static Singleton6 instance=new Singleton6();
    }
    //调用getInstance方法,事实上是获得Holder的instance静态属性
    public static Singleton6 getInstance(){
        return Holder.instance;
    }
}

7.枚举方式

package mutithread.concurrent.chapter14;

/**
 * 枚举方式   使用枚举类实现单例设计模式
 * @author zhangjinglong
 * @date 2020-02-19-20:44
 *枚举类不能够懒加载,对Singleton主动使用
 * 比如调用其中对静态方法则INSTANCE会立即得到实例化
 */
//枚举类型本身就是final的,不允许被继承
public enum Singleton7 {
    INSTANCE;

    //实例变量
    private byte[] data=new byte[1024];

    Singleton7(){
        System.out.printf("INSTANCE will be initialized immediately");
    }

    public static void method(){
        //调用该方法则会主动使用Single,INSTANCE将会被实例化
    }

    public static Singleton7 getInstance(){
        return INSTANCE;
    }
}

8.Holder方式+枚举

package mutithread.concurrent.chapter14;

/**
 *
 * 增加懒加载的特性,类似于Holder的方式
 * @author zhangjinglong
 * @date 2020-02-19-20:52
 */

public class Singleton8 {
    //实例变量
    private byte[] data=new byte[1024];

    private Singleton8(){

    }

    //使用枚举冲淡holder
    private enum EnumHolder{
        INSTANCE;
        private Singleton8 instance;

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

        private Singleton8 getInstance(){
            return instance;
        }
    }

    public static Singleton8 getInstance(){
        return EnumHolder.INSTANCE.getInstance();
    }

}

小结

本文收纳了8种单例模式,
第5种Volatile+Double-Check方式,设计比较精巧,值得细品
其中第6种Holder方式的单例设计是最好的设计之一,也是目前使用比较广的设计之一。

©️2020 CSDN 皮肤主题: 黑客帝国 设计师: 上身试试 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值