单例模式的7种实现方式

多个线程要操作同一个对象,保证对象的唯一性,实例化过程只实例化一次;


解决的思路

  • 有一个实例化的过程(只执行一次),产生实例化对象;
  • 类外部不能new 这个对象;
  • 提供返回实例对象的方法;

饿汉式

在加载类时就实例化一个对象

public class Singleton1 {
    //加载该类的时候就创建
    private static Singleton1 singleton1=new Singleton1();

    //构造方法私有,外部不能new 对象
    private Singleton1(){

    }
    public static Singleton1 getIntance(){
        return singleton1;
    }
    }

特点

  • 在加载的时候已经被实例化,所以只实例化了一次,线程安全;
  • 没有延迟加载,如果实例化的对象长时间不用,占用空间;
  • 性能比较好;

懒汉式

用这个对象的时候才去实例化

public class HoonSingleton {
    private static HoonSingleton hoonSingleton=null;
    private HoonSingleton(){

    }

    public static HoonSingleton getIntance() throws InterruptedException {
        if(hoonSingleton==null){
            hoonSingleton=new HoonSingleton();
        }
        return hoonSingleton;
    }
}

并发环境下,存在线程安全问题

public class HoonSingleton {
    private static HoonSingleton hoonSingleton=null;
    private HoonSingleton(){

    }

    public static HoonSingleton getIntance() throws InterruptedException {
        if(hoonSingleton==null){
            //模拟并发环境线程阻塞
            Thread.sleep(1);
            hoonSingleton=new HoonSingleton();
        }
        return hoonSingleton;
    }
    public static void main(String[] args) {

        for (int i = 0; i < 20; i++) {
            new Thread(new Runnable() {
                public void run() {
                    HoonSingleton intance = null;
                    try {
                        intance = HoonSingleton.getIntance();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(intance);
                }
            }).start();
        }
    }
}

可以看到实例化出了多个对象
在这里插入图片描述

懒汉式+同步方法

将实例化过程放到同步方法中

public class HoonSingleton {
    private static HoonSingleton hoonSingleton=null;
    private HoonSingleton(){
    }

    public synchronized static HoonSingleton getIntance() throws InterruptedException {
        if(hoonSingleton==null){
            hoonSingleton=new HoonSingleton();
        }
        return hoonSingleton;
    }
 }

保证每次第一次实例化只有一个线程执行,后面的线程不执行;但是这样锁的粒度太粗了,退化成了串行执行;

Double-Check-Locking

减小锁的粒度

public class HoonDoubleSyncSingleton {
    private static HoonDoubleSyncSingleton hoonSingleton=null;
    private HoonDoubleSyncSingleton(){
    }

    public static HoonDoubleSyncSingleton getIntance() throws InterruptedException {
        //第一次检索:挡住后面大多数线程
        if(hoonSingleton==null){ 
            synchronized (HoonDoubleSyncSingleton.class){
                //第二次检索:如果有多个线程通过第一次检索,则只有第一个线程可以实例化
                if (hoonSingleton==null){ 
                    hoonSingleton=new HoonDoubleSyncSingleton();
                }
            }
        }
        return hoonSingleton;
    }

保证了单例,但是没有保证指令重排序所带来的问题;

volatile-Douche-check

伪代码:

public class HoonDoubleSyncSingleton {
	//模拟数据库、socket连接
	static sqlConnection;
	static socket;
    private static HoonDoubleSyncSingleton hoonSingleton=null;
    private HoonDoubleSyncSingleton(){
    	sqlConnection;
    	socket;
    	new  HoonDoubleSyncSingleton();
    }

    public static HoonDoubleSyncSingleton getIntance() throws InterruptedException {
        //第一次检索:挡住后面大多数线程
        if(hoonSingleton==null){ 
            synchronized (HoonDoubleSyncSingleton.class){
                //第二次检索:如果有多个线程通过第一次检索,则只有第一个线程可以实例化
                if (hoonSingleton==null){ 
                    hoonSingleton=new HoonDoubleSyncSingleton();
                }
            }
        }
        return hoonSingleton;
    }

如果发生指令重排序,即先创建对象,然后再建立连接,如果这时对象需要使用连接,则会出现空指针异常。
在这里插入图片描述

解决方法

在这里插入图片描述
用volatile修饰对象,内存屏障保证在创建对象前先建立连接。

Holder

声明类的时候,成员变量中不生命实例变量,而放到静态内部类中;

public class HolderDemo {
    
    private static class Holder{
        private static HolderDemo insance=new HolderDemo();
    }
    
    public static HolderDemo getInstance(){
        return Holder.insance;
    }
}

只有当调用getInstance方法时,才开始加载静态内部类,开始实例化对象,保证了懒加载;而且静态类被存放在方法区,所以对象只有一个而且在共享数据区。

枚举

public class EumSingleton {


    private enum EnumDemo{
        INSTANCE;
        private static EumSingleton instance=new EumSingleton();
        private EumSingleton getInstance(){
            return instance;
        }
    }

    public static EumSingleton getInstance(){
        return EnumDemo.INSTANCE.getInstance();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值