java学习 单例模式

单例模式:确保某个类的实例有且只有一个

特点:

  1. 单例模式只能有一个实例。

  2. 单例类必须创建自己的唯一实例。

  3. 单例类必须向其他对象提供这一实例。

实现方式:
(一):饿汉式

public class Singleton1 {
	// 饿汉式 : 类加载时就创建对象,天生线程安全
	static final Singleton1 instance = new Singleton1();

	private Singleton1() {

	}

	public static Singleton1 getInstance() {
		return instance;
	}

}

(二):懒汉式

class Singleton2 {
	// 懒汉式 : 不用时不创建
	// 线程不安全
	private static Singleton2 instance = null;

	private Singleton2() {

	}

	public static Singleton2 getInstance() {
		if (instance == null) {
			instance = new Singleton2();
		}

		return instance;

	}

}

由于线程不安全,因此需要加锁

(三)加锁懒汉式:

public class Singleton2 {
	// 懒汉式 : 不用时不创建
	// 线程不安全,需要同步操作
	// 加锁影响性能
	private static volatile Singleton3 instance = null;

	private Singleton3() {

	}

	public static synchronized Singleton3 getInstance() {
		if (instance == null) {
			instance = new Singleton3();
		}

		return instance;

	}

}

(四)双重检查锁:

class Singleton4{
	// 双重检查锁
	private static volatile Singleton4 instance;
	
	private Singleton4() {
		
	}
	
	public static Singleton4 getInstace() {
		if(instance == null ) {
			synchronized(Singleton4.class) {
				if(instance == null) {
					instance = new Singleton4();
				}
			}
		}
		return instance;
	}
}

在并发时,双重检查锁的情景:

STEP 1. 线程A访问getInstance()方法,因为单例还没有实例化,所以进入了锁定块。

STEP 2. 线程B访问getInstance()方法,因为单例还没有实例化,得以访问接下来代码块,而接下来代码块已经被线程1锁定。

STEP 3. 线程A进入下一判断,因为单例还没有实例化,所以进行单例实例化,成功实例化后退出代码块,解除锁定。

STEP 4. 线程B进入接下来代码块,锁定线程,进入下一判断,因为已经实例化,退出代码块,解除锁定。

STEP 5. 线程A初始化并获取到了单例实例并返回,线程B获取了在线程A中初始化的单例。

理论上双重校验锁法是线程安全的,并且,这种方法实现了lazyloading。

(五):静态内部类写法

class Singleton5{
	// 使用内部类 用时再创建
	// 天生线程安全
	private static class Holder {
		private static final Singleton5 instance = new Singleton5();
	}

	private Singleton5() {

	}

	public static Singleton5 getInstance() {
		return Holder.instance;
	}
}

(六):枚举法创建单例,代码最简洁的一种写法


public enum Singleton6{
    instance;
    public void whateverMethod(){}    
}

调用时只需Singleton6.instance即可

上面提到的其他几种实现单例的方式都有共同的缺点:

1)需要额外的工作来实现序列化,否则每次反序列化一个序列化的对象时都会创建一个新的实例。

2)可以使用反射强行调用私有构造器(如果要避免这种情况,可以修改构造器,让它在创建第二个实例的时候抛异常)。

而枚举类很好的解决了这两个问题,使用枚举除了线程安全和防止反射调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的对象。

关于枚举类更详细的内容可以参考下面的文章
https://blog.csdn.net/javazejian/article/details/71333103

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值