单例模式
特点:
- 在JVM中,单例模式能保证对象实例只有一个
- 构造器必须是私有的,外部无法通过构造器创建对象实例
- 没有公开的set方法,外部不能通过set方法进行对象的创建
- 对外公开提供一个get方法,通过get方法可以获取对象实例
优点:
- 某些类创建非常繁琐,使用单例模式避免对象的频繁创建,而造成性能损耗
- 省去了new操作符,减轻系统内存使用频率,减轻GC的压力
- 避免对资源的重复占用
饿汉式单例模式
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){};
public static Singleton getInstance(){
return instance;
}
}
为什么说是饿汉式呢?
因为它在类加载的时候就直接把实例创建了出来,后面的方法直接进行返回即可
应用场景:
适用于热点数据,在启动JVM的时候,就将热点数据创建好,这样也可以避免预热阶段,直接就可以使用了。
为什么是线程安全的?
因为类加载的方式是按需加载,且只加载一次,因此在访问单例对象的时候,其实单例对象的实例已经创建好了。因此饿汉式天生就是线程安全的。
懒汉式单例模式
public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
return instance;
}
}
懒汉式单例模式是非线程安全的
为什么叫懒汉式呢?
因为类加载的时候,并不会直接将这个实例创建出来,而是在需要使用的时候进行创建
使用场景
适用于那些不热门的数据,因为只有在需要使用的时候进行调用并创建,这样就不会造成不必要的空间浪费
为什么是非线程安全的呢?
这里假设有两个线程,第一个线程和第二个线程现在都进行了if的判断,进入到了对象的创建,那么这两个线程就会创建两个对象。
那么针对这个懒汉式的非线程安全使用下面加锁的方式进行解决
懒汉式加锁
public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance ==null){
instance = new Singleton();
}
return instance;
}
}
这样直接在获取对象实例的方法上加锁,虽然保证了线程的安全,但是这样加锁性能太低了,那么如何解决呢???
懒汉式加锁进阶
public class Singleton{
private static Singleton instance =null;
private Singleton(){}
public static Singleton getInstance(){
if(instance ==null){
synchronized(Singleton.class){
if(instance ==null){
instance = new Singleton();
}
}
}
return instance;
}
}
这样把锁加在方法内,而且进行了一次判断,这样性能比上一个性能有提高,但是JVM对代码的运行会有运行优化,也就是在运行期间会进行指令重排操作,意思就是比如现在一个线程已经创建好了实例,而且JVM内存中也开辟了空间给这个实例,但是还没有初始化完成,而这个时候另一个线程判断instance不为空,那么直接返回进行调用,发现还没有实例化而出现错误,那么又如何解决这个问题呢???
懒汉式加锁进阶加强(volatile)
public class Singleton{
private static volatile Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance ==null){
synchronized(Singleton.class){
if(instance==null){
instance = new Singleton();
}
}
}
return instance;
}
}
volatile主要是防止代码运行时的指令重排,可以保证,在创建实例完成,并且初始化之后,另外的线程才能对实例进行使用,那么这个已经很完美了,但是还有比这个更好的方式进行实现吗??
基于静态内部类实现的单例模式
public class Singleton{
private Singleton(){}
private static class SingletonFactory{
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonFactory.instance;
}
}
基于枚举的单例模式
class enum Singleton{
Instance;
}
为什么使用单例模式而不使用静态类呢?
- 单例模式有懒汉模式,可以按需进行资源加载,而静态类会直接加载进内存中
- 静态类每次创建使用之后,都会被GC掉,而单例对象不会被GC