设计模式-单例模式

单例模式:Singleton

单例模式的多种写法:
最常用的是第1种: 饿汉式, 写法简洁, 实际开发中常用.
最完美的是第5种:枚举的方式, 无懈可击

1. 饿汉式

public class Singleton_1 {
	/**
	 * 私有化构造方法,不能在外部new实例
	 */
	private Singleton_1() {
	}

	/**
	 * 静态成员变量:在类加载的时候进行实例化
	 * JVM只会对同一个类加载一次,保证了只会有一个person的实例对象
	 * 私有化的构造方式,在类内部还是可以被调用的
	 */
	private static final Singleton_1 one = new Singleton_1();

	public static Singleton_1 getInstance(){
		return one;
	}
}
public class Test {
	public static void main(String[] args) {
		//构造方法私有化,无法new
		//Singleton_1 singleton = new Singleton_1();

		Singleton_1 instance = Singleton_1.getInstance();
		Singleton_1 instance1 = Singleton_1.getInstance();
		
		System.out.println(instance == instance1);
		//输出:true
	}
}

不足: 只要类加载时,对象就会被实例化, 无论是否要被使用, 浪费资源.

2. 懒汉式

public class Singleton_2 {
	/**
	 * 私有化构造方法,不能在外部new实例
	 */
	private Singleton_2() {
	}

	/**
	 * 只有当getInstance方法被调用时,才被实例化
	 */
	private static Singleton_2 one;
	public static Singleton_2 getInstance(){
		if (one == null){
			one = new Singleton_2();
		}
		return one;
	}
}
public class Test {
	public static void main(String[] args) {
		Singleton_2 instance = Singleton_2.getInstance();
		Singleton_2 instance1 = Singleton_2.getInstance();
		System.out.println(instance == instance1);
		//输出:true
	}
}

不足: 多线程访问时, 会存在不同的实例化对象.

public class Test {
	public static void main(String[] args) {
		for (int i = 0; i < 5; i++) {
			new Thread(() -> {
				Singleton_2 instance = Singleton_2.getInstance();
				System.out.println(instance.hashCode());
			}).start();
			//输出:
			//     1158246010
			//     736763851
			//     275307607
			//     736763851
			//     1158246010
		}
	}
}

	//这里为了效果明显, 睡了一下
	//原因就是:
	//线程1进入one == null的if判断,但没来得及new的时候,进程被停止;
	//线程2执行,同样进入了one == null的if判断.这样两个线程最后均会new出来一个实例对象
	public static Singleton_2 getInstance() {
		if (one == null){
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			one = new Singleton_2();
		}
		return one;
	}

3. 使用synchronized锁的懒汉式

第一种: 给getInstance方法加锁

//整个方法都是阻塞的,同一时间只能有一个线程执行该方法, 最耗时间.
	public static synchronized Singleton_2 getInstance() {
		if (one == null){
			one = new Singleton_2();
		}
		return one;
	}

第二种: 双重判断 + 加锁
(DCL: double check lock), 变量one必须使用volatile关键字, 多线程场景下指令重排会导致return了半初始化状态下的Singleton_2对象

//相对于前一种加锁方式, 减小了加锁的范围, 执行效率更高
	public static synchronized Singleton_2 getInstance() {
		if (one == null){
			synchronized ("lock"){
				if (one == null){
					one = new Singleton_2();
				}
			}
		}
		return one;
	}

4. 以内部类实现的懒汉式

public class Singleton_3 {
	/**
	 * 私有化构造方法,不能在外部new实例
	 */
	private Singleton_3() {
	}

	//方法调用时,SingletonBuilder加载,singleton被实例化
	//同时由jvm保证了SingletonBuilder只会被加载一次,即singleton只实例化一次
	public static Singleton_3 getInstance(){
		return SingletonBuilder.singleton;
	}

//通过静态内部类来实例化Singleton_3对象:
//在Singleton_3加载时,静态内部类不会被加载,因此singleton不会实例化
	private static class SingletonBuilder{
		private static Singleton_3 singleton = new Singleton_3();
	}
}

在这里插入图片描述
缺点: 可以通过反射或反序列化的方式, 获得不同的实例对象(前述几种单例的写法也是).

5. 枚举实现


public enum Singleton_4 {
	ONE;
	private String name ="枚举单例模式";
	public void doing(){
		System.out.println(this.name);
	}
}

来自《Effective Java》的单例模式写法,可以有效防止以反射或反序列化的方式产生不同的实例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值