单例模式详解

为什么需要使用单例模式?

对于某些庞大的类,频繁的创建销毁对象会耗费大量资源,并且这些对象是可以复用的。如:连接数据库的对象。

懒汉式

懒汉式创建对象:

java 复制代码
public class Singleton{
    
    private static final Singleton singleton;
    
    private Singleton(){}
    
    public static Singleton getInstance() {
    	if(singleton==null){//存着线程安全问题
    		singleton=new Singleton();
    	}
        return singleton;
    }
}

上面这种模式会出现线程安全的问题:一般会在getInstance()加上synchronized确保线程安全,但是每次获取对象都要先获取锁,并发性能差。

java 复制代码
public class Singleton{
    
    private static final Singleton singleton;
    
    private Singleton(){}
    
    public static synchronized Singleton getInstance() {//加上synchronized保证线程安全,但是并发性能差
    	if(singleton==null){//存着线程安全问题
    		singleton=new Singleton();
    	}
        return singleton;
    }
}

由于在每次获取对象时都要进行同步,可以在创建对象时进行同步

java 复制代码
public class Singleton{
    
    private static final Singleton singleton;
    
    private Singleton(){}
    
    public static  Singleton getInstance() {
    	if(singleton==null){//存着线程安全问题,可能多个线程进入,虽然后面后线程同步,但是仍可能创建多个对象
    		synchronized(Singleton.class){//创建对象时进行同步
    			singleton=new Singleton();
    		}
    		
    	}
        return singleton;
    }
}

但是上述代码存在线程安全问题,为了保证线程安全,进入代码块后再次进行判空操作,即双检锁

java 复制代码
public class Singleton{
    
    private static final Singleton singleton;
    
    private Singleton(){}
    
    public static  Singleton getInstance() {
    	if(singleton==null){//第一次判空,保证创建对象时才进行同步
    		synchronized(Singleton.class){//创建对象时进行同步
    			if(singleton==null){//第二次判空,保证线程安全
    				singleton=new Singleton();
    			}
    		}
    	}
        return singleton;
    }
}

上面的代码仍然会发生指令重排的问题。

singleton=new Singleton();在指令层面不是原子操作:①分配内存②初始化对象③对象指向内存地址。在真正执行时,虚拟机为了效率可能会发生指令重排比如①③②,如果线程1执行完①③还没有执行②,此时线程2获取对象会获取到没有初始化的对象,会造成线程不安全的情况。

解决方法:加上volatile修饰,阻止指令重排。

java 复制代码
public class Singleton{
    
    private volatile static final Singleton singleton;//加上volatile进行修饰,
    
    private Singleton(){}
    
    public static  Singleton getInstance() {
    	if(singleton==null){//第一次判空,保证创建对象时才进行同步
    		synchronized(Singleton.class){//创建对象时进行同步
    			if(singleton==null){//第二次判空,保证线程安全
    				singleton=new Singleton();
    			}
    		}
    	}
        return singleton;
    }
}

上面虽然满足了懒加载和线程安全,但是写法比较复杂。可以通过静态内部类来满足懒加载,线程安全,简单这三个条件。

java 复制代码
public class Singleton{
    private static class SingletonHolder{//静态内部类在程序启动的时候不会加载,只有第一次调用的时候才会被加载
    	private static final Singleton INSTANCE = new Singleton();
    }
    
    private Singleton(){}
    
    public static  Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
相关推荐
NorthCastle17 小时前
设计模式-创建型模式-单例模式详解
java·单例模式·设计模式
听风吟丶17 小时前
《深入理解 Java 中的单例模式(Singleton)》
java·单例模式
桂月二二17 小时前
Java编程中的设计模式:单例模式的深入解析与应用
java·单例模式·设计模式
西岭千秋雪_2 天前
设计模式の单例&工厂&原型模式
java·单例模式·设计模式·简单工厂模式·工厂方法模式·抽象工厂模式·原型模式
fuvuof2 天前
设计模式——单例模式和工厂模式
python·单例模式·设计模式
daiyang123...3 天前
JavaEE 【知识改变命运】05 多线程(4)
java·单例模式·java-ee
WangMing_X3 天前
C# 23种设计模式(1)单例模式(单件模式)
单例模式·设计模式
wkj0013 天前
PHP开发设计模式:单例模式
单例模式·设计模式·php
hope_wisdom3 天前
实战设计模式之单例模式
单例模式·设计模式·架构·系统架构·软件工程
Jack_abu4 天前
设计模式学习之——单例模式
单例模式·设计模式