单例模式解析

单例模式的作用

主要解决一个全局使用的类频繁的创建和消费,从而提升代码的整体性能。

单例模式的运用场景

1.数据库的连接池不会被反复创建;
2.spring中一个单例模式bean的生成和使用;
3.代码中需要设置全局的一些属性保存;

七种单例模式的实现

1.静态类的使用

public calss Singleton {
	public static Map<String, String> cache = new ConcurrentHashMap<>();
}

1.这样静态类的方法可以在第一次运行的时候直接初始化Map,不需要到延迟加载再使用;
2.在不需要维持任何状态下,仅仅用于全局访问,这样使用静态类的方式更加方便;
3.如果需要被继承以及需要维持一些特定的状态,适合使用单例模式;

2.懒汉模式(线程不安全)

public calss Singleton {
	private static Singleton instance;
	private Singleton() {

	}
	public static Singleton getInstance() {
		if(null != instance) {
			return instance;
		}
		intance = new Singleton();
		return instance;
	}

}

1.单例模式不允许外部直接创建,也就是new Singleton();所以默认的构造函数添加了私有属性private;
2.此方式的单例模式满足了懒加载,但如果多个访问者同时去获取对象实例,就会造成多个同样的实例并存,从而没有达到单例模式的要求;

3.懒汉模式(线程安全)

public calss Singleton {
	private static Singleton instance;
	private Singleton() {

	}
	public static synchronized Singleton getInstance() {
		if(null != instance) {
			return instance;
		}
		instance = new Singleton();
		return instance;
	}

}

此种方式虽然是安全的,但由于锁加在方法上,所有的访问都需要锁占用导致资源的浪费,如果不是特定情况下,不建议此方式实现单例模式;

4.饿汉模式(线程安全)

public calss Singleton {
	private static Singleton instance = new Singleton();
	private Singleton() {
	
	}
	public static Singleton getInstance() {
		return instance;
	}

}

1.此方式在程序启动的时候直接加载,外部需要使用的时候直接获取即可;
2.并不是懒加载,也就是不管你使用不使用,在程序启动之初都会创建;
3.那么这种⽅式导致的问题就像你下载个游戏软件,可能你游戏地图还没有打开呢,但是程序已经将
这些地图全部实例化。到你⼿机上最明显体验就⼀开游戏内存满了,⼿机卡了,需要换了。

5.使用类的内部类(线程安全)

public calss Singleton {
	private static calss SingletonHolder {
		private static Singleton instance = new Singleton();
	}
	private Singleton() {

	}
	public static Singleton getInstance() {
		return SingletonHolder.instance;
	}
}

1.使用类的静态内部类实现的单例模式,既保证了线程安全又保证了懒加载,同时不会因为加锁的方式消耗性能;
2.因为java虚拟机可以访问多线程并保证访问的正确性,也就是一个类的构造方法在多线程环境下可以被正确的加载;
3.十分推荐使用此种方法实现单例模式;

6.双重锁校验(线程安全)

public calss Singleton {
	private static Singleton instance;
	private Singleton() {

	}
	public static synchronized Singleton getInstance() {
		if(null != instance) {
			return instance;
		}
		synchronized(Singleton.calss) {
			if(null == instance) {
				instance = new Singleton();
			}
		}
		return instance;
	}
}

1.双重锁的方式时方法锁的优化,减少了部分获取实例的耗时;
2.该方式满足懒加载;

7.CAS「AtomicReference」(线程安全)

public calss Singleton {
	private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<>();
	private static Singleton instance;
	private Singleton() {

	}
	public static final Singleton getInstance() {
		for(; ;) {
			Singleton instance = INSTANCE.get;
			if(null != instance) {
				return instance;
			}
			INSTANCE.compareAndSet(null, new Singleton());
			return INSTANCE.get;
		}
	}
	public static void main(String[] args) {
		System.out.println(Singleton.getInstance());
	}
}

1.java并发库提供了许多原子类来支持并发访问的数据安全性:AtomicInteger 、 AtomicBoolean 、 AtomicLong 、 AtomicReference 。
2.AtomicReference 可以封装引⽤⼀个V实例,⽀持并发访问如上的单例⽅式就是使⽤了这样的⼀个
特点。
3.使⽤CAS的好处就是不需要使⽤传统的加锁⽅式保证线程安全,⽽是依赖于CAS的忙等算法,依赖
于底层硬件的实现,来保证线程安全。相对于其他锁的实现没有线程的切换和阻塞也就没有了额外
的开销,并且可以⽀持较⼤的并发性。
4.当然CAS也有⼀个缺点就是忙等,如果⼀直没有获取到将会处于死循环中。

8.Effective Java作者推荐的枚举单例(线程安全)

public enum Singleton {
 INSTANCE;
 public void test(){
 System.out.println("hi~");
 }
}

1.Effective Java 作者推荐使⽤枚举的⽅式解决单例模式,此种⽅式可能是平时最少⽤到的。
2.这种⽅式解决了最主要的;线程安全、⾃由串⾏化、单⼀实例。

调试方式

@Test
public void test() {
 Singleton.INSTANCE.test();
 }

这种写法在功能上与共有域⽅法相近,但是它更简洁,⽆偿地提供了串⾏化机制,绝对防⽌对此实例
化,即使是在⾯对复杂的串⾏化或者反射攻击的时候。虽然这种⽅法还没有⼴泛采⽤,但是单元素的枚
举类型已经成为实现Singleton的最佳⽅法。但也要知道此种⽅式在存在继承场景下是不可⽤的。

总结

1.虽然只是⼀个很平常的单例模式,但在各种的实现上真的可以看到java的基本功的体现,这⾥包括了;懒汉、饿汉、线程是否安全、静态类、内部类、加锁、串⾏化等等。
2.在平时的开发中如果可以确保此类是全局可⽤不需要做懒加载,那么直接创建并给外部调⽤即可。
但如果是很多的类,有些需要在⽤户触发⼀定的条件后(游戏关卡)才显示,那么⼀定要⽤懒加载。
线程的安全上可以按需选择。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值