目录
什么是单例模式?
定义
单例模式(Singleton Pattern)是一种创建型设计模式,它保证一个类只有一个实例,并提供一个全局访问点以供外部代码使用。
单例模式的主要特点
保证了类只有一个实例。由于只有一个实例存在,因此可以避免多个实例之间的冲突。
提供了一个全局访问点。通过单例模式,可以在任何需要时访问该类的唯一实例,方便调用和使用。
能够解决频繁创建和销毁全局使用的类实例的问题。(如果一个对象已经被创建了,那么以后每次请求该对象时,都会直接返回之前创建好的对象实例,避免了重复创建和销毁对象的开销,提高系统性能。)
单例模式的几种设计模式
懒汉式:在首次使用时创建实例。
饿汉式:在类加载时就创建实例。
双重校验锁:采用双锁机制,安全且在多线程情况下能保持高性能。
1.懒汉式:线程不安全
这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式
public class LanHan { private LanHan(){} private static LanHan lanHan; public static LanHan getLanHan(){ if(lanHan==null){ lanHan = new LanHan(); } return lanHan; } public void sayHello(){ System.out.println("hello world"); } }
public class Test { public static void main(String[] args) { LanHan lanHan = LanHan.getLanHan(); lanHan.sayHello(); } }
2.懒汉式:线程安全
这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
public class LanHan { private LanHan(){} private static LanHan lanHan; public static synchronized LanHan getLanHan(){ if(lanHan==null){ lanHan = new LanHan(); } return lanHan; } public void sayHello(){ System.out.println("hello world"); } }
public class Test { public static void main(String[] args) { LanHan lanHan = LanHan.getLanHan(); lanHan.sayHello(); } }
3.饿汉式
这种方式比较常用,但容易产生垃圾对象。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
public class EHan { private EHan(){} private static EHan eHan = new EHan(); public static EHan getEHan(){ return eHan; } public void syHello(){ System.out.println("hello world"); } }
public class Test { public static void main(String[] args) { LanHan lanHan = LanHan.getLanHan(); lanHan.sayHello(); EHan eHan = EHan.getEHan(); eHan.syHello(); } }
4.双重校验锁
这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
public class DoubleLock { private static volatile DoubleLock doubleLock; private DoubleLock(){} public static DoubleLock getDoubleLock(){ if(doubleLock==null){ synchronized (DoubleLock.class){ doubleLock = new DoubleLock(); } } return doubleLock; } public void sayHello(){ System.out.println("hello world"); } }
public class Test { public static void main(String[] args) { LanHan lanHan = LanHan.getLanHan(); lanHan.sayHello(); EHan eHan = EHan.getEHan(); eHan.syHello(); DoubleLock.getDoubleLock().sayHello(); } }
单例模式的优缺点
优点:
1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
2、避免对资源的多重占用(比如写文件操作)。
缺点:
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
适用场景:
需要全局访问和管理的资源,可以确保资源的唯一性和一致性。
例如:
1、要求生产唯一序列号。
2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。