📝个人主页:五敷有你
🔥系列专栏:设计模式
⛺️稳中求进,晒太阳
单例模式
单例模式属于创建者模式,是Java中最简单的设计模式之一,这种设计模式属于创建型模式,提供了一种创建对象的最佳方式。
该模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建,这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供了一个全局访问点来访问该实例。
注意:
-
单例只能有一个实例。
-
单例类必须自己创建自己的唯一实例。
-
单例类必须给所有其他对象提供这一实例。
优点:
-
内存中只有一个实例,减少内存开销,尤其是频繁创建和销毁实例时。
-
避免资源的多重占用。
缺点:
-
没有接口,不能继承。
-
与单一职责冲突,一个类应该只关心内部逻辑,而不关心实力化方式。
饿汉式
饿汉式:直接创建出类的实例化,然后用private私有化,对外只用静态方法暴露。
静态变量
步骤
-
构造器私有化
-
类的内部创建对象
-
向外暴露一个静态的公共方法
优点:写法简单,在类加载的时候完成实例化,避免了线程同步的问题。
缺点:类加载完成实例化,没有达到LazyLoading的效果,若该实例从未使用,会造成内存浪费。
class Singleton {
//私有化构造器
private Singleton() {
}
//内部创建对象实例
private final static Singleton instance = new Singleton();
//对外公有的静态方法
public static Singleton getInstance() {
return instance;
}
}
静态代码块
public class Hungry {
private static Hungry instance;
static {
instance=new Hungry();
}
private Hungry(){};
public static Hungry getInstance(){
return instance;
}
}
懒汉式
所谓懒汉式,就是在需要调用的时候再创建类的实例化。
线程不安全
起到了懒加载效果,但是只能在单线程使用,多线程会不安全,因为当多个线程并发同时判断instance为空时,就会相应的实例化多个对象。
/**
* 线程不安全
*/
public class LazyLoadMode {
private static LazyLoadMode instance;
private LazyLoadMode(){};
public static LazyLoadMode getInstance(){
if(instance==null){
instance=new LazyLoadMode();
}
return instance;
}
}
如下:线程不安全。
线程安全
不安全加个锁不就安全了,使用synchronized关键字。
这样虽然解决了线程安全,但其实实例化操作只做一次,而获取实例(即getInstance)的操作是很多次的,把调用的方法加上同步,会大大降低效率。
每次都会加锁解锁,效率太低了。
/**
* 线程不安全
*/
public class LazyLoadMode {
private static LazyLoadMode instance;
private LazyLoadMode(){};
public static synchronized LazyLoadMode getInstance(){
if(instance==null){
instance=new LazyLoadMode();
}
return instance;
}
}
双重检查
上面代码效率低,那在同步前判断一下有没有实例化不就好了,没有实例化就new一个,否则直接return即可。
需要使用volatile,防止指令重排序,如果不用volatile,就会和线程不安全情形一样,在if判断那会有并发,导致new了多个实例化。。
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) { //判断是否实例化
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton; //否则直接return
}
}
这样既实现了懒加载,又保证了线程安全。
静态内部类
静态内部类在外部类装载时不会实例化,当调用的时候才会装载并实例化,且JVM保证了其装载时的线程安全性。也能保证懒加载和线程安全,有点像自带版的双重检查。
class Singleton {
private static volatile Singleton instance;
private Singleton() {}
//静态内部类,包含一个静态属性:Singleton
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
//对外公有的静态方法,直接返回SingletonInstance.INSTANCE
public static synchronized Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
枚举
其实,使用枚举也能实现单例模式,不仅能避免多线程同步问题,也能防止反序列化重新创建新的对象。
enum Singleton {
INSTANCE; //属性
public void say() {
System.out.println("记得三连~");
}
}