文章目录
前言
作者目前在复习总结设计模式,便计划写一个设计模式的专题,本篇介绍的是单例模式,主要介绍的是7种单例模式的实现。作者写博客主要是为了归纳总结技术,加深对它的理解,如有任何错误地方请帮忙指出。
单例模式
1.定义
单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。
2.特点
- 单例类只有一个实例对象;
- 该单例对象必须由单例类自行创建;
- 单例类对外提供一个访问该单例的全局访问点。
3.优缺点
优点:
- 单例模式可以保证内存里只有一个实例,减少了内存的开销。
- 可以避免对资源的多重占用。
- 单例模式设置全局访问点,可以优化和共享资源的访问。
缺点:
- 单例模式一般没有接口,扩展困难。
- 在并发测试中,单例模式不利于代码调试。
- 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。
4.应用场景
- 需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 GC。
- 某类只要求生成一个对象的时候,如每个人的身份证号等。
- 某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。
- 某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
5.当对象需要被共享的场合。
实现方式
1.懒汉模式 (线程不安全)
/**
* 懒汉模式 (线程不安全)
* @author winter
*/
public class Singleton_01 {
private static Singleton_01 instance;
private Singleton_01() {
}
public static Singleton_01 getInstance(){
if (null != instance) {
return instance;
}
return new Singleton_01();
}
}
特点:不能再外部创建,也就是new Singleton_01()。
问题:线程不安全,多个访问者同时获取对象实例,就会造成多个同样的实例并存。
2.懒汉模式 (线程安全)
/**
* 懒汉模式 (线程安全)
* @author winter
*/
public class Singleton_02 {
private static Singleton_02 instance;
private Singleton_02() {
}
public static synchronized Singleton_02 getInstance() {
if (null != instance) {
return instance;
}
return new Singleton_02();
}
}
线程安全,直接将锁加在方法上,所有的访问都会抢锁占用,导致资源浪费,效率低,不推荐。
3.饿汉模式 (线程安全)
/**
* 饿汉模式 (线程安全)
* @author winter
*/
public class Singleton_03 {
private static Singleton_03 instance = new Singleton_03();
private Singleton_03() {
}
public static Singleton_03 getInstance() {
return instance;
}
}
在启动时候直接运行加载,后续有外部需要使用时直接获取即可,会造成一些内存浪费问题。
4.使用类的内部类 (线程安全)
/**
* 使用类的内部类 (线程安全)
* @author winter
*/
public class Singleton_04 {
private static class SingletonHolder {
private static Singleton_04 instance = new Singleton_04();
}
private Singleton_04() {
}
public static Singleton_04 getInstance() {
return SingletonHolder.instance;
}
}
既保证了线程安全,又保证了懒汉模式,同时不会因为加锁而降低性能。比较推荐的一种单例模式。
5.双重锁校验 (线程安全)
/**
* 双重锁校验 (线程安全)
* @author winter
*/
public class Singleton_05 {
private static Singleton_05 instance;
private Singleton_05() {
}
public static Singleton_05 getInstance(){
if(null != instance) return instance;
synchronized (Singleton_05.class){
if (null == instance){
instance = new Singleton_05();
}
}
return instance;
}
}
实现了对方法级锁的优化,减少了获取实例的耗时,也满足了懒汉模式。
6.CAS (线程安全)
/**
* CAS (线程安全)
* @author winter
*/
public class Singleton_06 {
private static final AtomicReference<Singleton_06> INSTANCE = new AtomicReference<Singleton_06>();
private static Singleton_06 instance;
private Singleton_06() {
}
public static final Singleton_06 getInstance() {
for (; ; ) {
Singleton_06 instance = INSTANCE.get();
if (null != instance) return instance;
INSTANCE.compareAndSet(null, new Singleton_06());
return INSTANCE.get();
}
}
}
在线程竞争不激烈时,相对其他锁的实现,没有线程的切换和阻塞也没有了额外的开销,并且可以支持较大的并发。但在在线程竞争激烈时,CAS会一直自旋。
7.枚举单例 (线程安全)
/**
* 枚举单例 (线程安全)
* @author winter
*/
public enum Singleton_07 {
INSTANCE;
public void doSomething() {
System.out.println("doSomething");
}
//调用方式
public static void main(String[] args) {
Singleton_07.INSTANCE.doSomething();
}
}
这种方式解决了线程安全、*穿行化和单一实例问题,并且非常简洁,是Joshua.J.Bloch推荐的单例模式。
总结
单例是设计模式中最简单的一种模式,但在各种的实现上却需要用到java的基本功,也是面试中高频率问到一种设计模式,如 1. 请写出一种高效安全的单例模式? 2. 懒汉模式是线程安全吗?为什么?
参考 《重新java设计模式》