场景
在一般开发中,常常要读取配置文件,生成一个配置对象,如果在多处应用到这个配置文件,那么就要创建很多该配置的实例,如果配置量比较大,会浪费大量内存
,单例模式使得在整个系统中,该对象都只有一份,在需要使用的时候直接获取已经被实例化的对象,避免内存浪费
单例模式
保证一个类只有一个实例,并提供一个全局访问点
,单例模式为了防止外部主动创建内,故而把构造方法设为私有
单例模式有两种实现方式
饿汉模式
:在类的初始化的时候就创建好对象,适用于对象不是很占内存的情景下
懒汉模式
:在使用的时候创建对象,适用于对象比较占内存的场景下
懒汉模式
懒汉模式由于是在需要使用的时候创建实例,所以要判断,这样就会产生同步问题,当线程A和B同时进入if语句但是没有创建实例时,这样就会产生两个实例,所以使用synchronized
防止同步问题,但是使用synchronized就会使得效率降低,而且还要做判断,这样我们可以使用双重检查加锁
来实现
双重检查加锁
:在进入方法前先不使用synchronized
进行同步,但是需要使用volatile
修饰,使用volatile
会使得该变量具有可见性,被volatile修饰的变量
修饰的变量会使得其他线程本地的该变量失效重新从内存中读取最新的被修改后的变量
使用synchronized修饰方法时,线程每次获取实例时,都会阻塞其他线程
,而使用synchronized修饰代码块时,先判断是否为空,如果为空才进入同步代码块,这样就只有在第一次实例化的时候才会阻塞线程
,其他时候就不需要进入同步代码块了,缺点就是volatile本身会屏蔽虚拟机中的一些优化代码
,所以也会降低一定的效率
package singleton;
/**
* 懒汉模式
*/
public class Singleton {
/**
* 定义一个变量来存储创建好的类实例
*/
private static Singleton uniqueInstance = null;
/**
* 私有化构造方法
*/
private Singleton(){}
/**
* 定义一个方法来为客户端提供类实例
*/
public static synchronized Singleton getInstance(){
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
/**
* 优化
*/
public static Singleton getInstance2 (){
if(uniqueInstance == null){
//同步块,即使有两个线程进来也只允许一个线程创建实例
synchronized (Singleton.class){
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
更高校的懒汉模式
在上面的方法中,虽然优化了synchronized
带来的副作用,但是又引入了volatile
,它会屏蔽JVM自身的一些优化代码
通过静态内部类实现懒汉模式:能通过内部类实现懒汉模式的最主要的条件是 JVM在有些流程中已经保证了线程安全,例如在类加载的时候
属于线程安全,所以我们可以在类中定义一个静态内部类,在静态内部类中实例化单例类,这样,当你没有获取实例时,该类不会被加载,自然内部类也不会被加载,内部类不被加载该实例就不会被初始化
package singleton;
public class Singleton3 {
/**
* 类级的内部类,也就是静态的成员式内部类,和其他被static修饰的成员类似
* 属于类级别的,和对象无关,只有在被调用的时候才会被装载,由于类被装载
* 时属于线程安全的,即由JVM内部为我们保证线程安全
*/
private static class SingletonHolder{
/**
* 静态初始化器,有JVM来保证线程安全
*/
private static Singleton3 instance = new Singleton3();
}
/**
* 私有化构造方法
*/
private Singleton3(){ }
public static Singleton3 getInstance(){
return SingletonHolder.instance;
}
}
饿汉模式
package singleton;
/**
* 饿汉模式
*/
public class Singleton2 {
/**
* 定义一个变量存储实例,在饿汉模式中直接实例化
*/
private static Singleton2 uniqueInstance = new Singleton2();
/**
* 私有化构造方法
*/
private Singleton2(){}
/**
* 提供一个方法为客户端提供实例
*/
public static Singleton2 getInstance(){
return uniqueInstance;
}
}