单例设计模式详解

单例设计模式要点:

一是某个类只能有一个实例;
构造器私有化

二是它必须自行创建这个实例;
含有一个该类的静态变量来保存这个唯一的实例

三是它必须自行向整个系统提供这个实例;
对外提供获取该实例对象的方式:
(1)直接暴露(2)用静态变量的get方法获取

常见的几种形式:

饿汉式:直接创建对象,不存在线程安全问题

直接实例化饿汉式(简洁直观)
枚举式(最简洁)
静态代码块饿汉式(适合复杂实例化)

懒汉式:延迟创建对象

线程不安全(适用于单线程)
线程安全(适用于多线程)
静态内部类形式(适用于多线程)

代码示例:
1.直接实例化饿汉式
public class Singleton1 {
	public static final Singleton1 INSTANCE = new Singleton1();
	private Singleton1(){
		
	}
}
2.枚举式
public enum Singleton2 {
	INSTANCE
}
3.静态代码块饿汉式(适合复杂情况,静态代码块中还可以写其他业务)
public class Singleton3 {
	public static final Singleton3 INSTANCE;
	
	static{
		INSTANCE = new Singleton3(); 
	}
	
	private Singleton3(){
		
	}
}

以上的创建方式,调用时都是在main方法中直接用类名.(点)的形式即可,比如

Singleton3 singleton3 = Singleton3.INSTANCE;

**总结:**饿汉式不存在线程安全问题,这是由其类加载机制来保证的。



饿汉式的问题:有可能程序运行时并不需要对象,这样的话,使用饿汉式创建会影响效率,占用资源。于是就有了懒汉式(饱汉式)

以下是懒汉式设计方法:

1.线程不安全(适用于单线程)
//在方法中去new一个对象,调用方法的时候才会创建对象
public class Singleton4 {
	private static Singleton4 instance;
	private Singleton4(){
		
	}
	
	public static Singleton4 getInstance(){
		if(instance == null){
			instance = new Singleton4();
		}
		return instance;
	}
}

接着写一个测试方法:

public class Test{
	public static void main(String[] args) {
		Singleton4 s1 = Singleton4.getInstance();
		Singleton4 s2 = Singleton4.getInstance();
		System.out.println(s1==s2);
		System.out.println(s1);
		System.out.println(s2);
	}
}

输出结果,单线程下线程安全
在这里插入图片描述

那么如果在多线程情况下,结果会怎么样呢,这时,我们创建一个多线程环境下的测试用例:

public class TestSingleton {
	public static void main(String[] args) throws InterruptedException, ExecutionException {		
		//多线程情况下
		Callable<Singleton4> c = new Callable<Singleton4>() {

			@Override
			public Singleton4 call() throws Exception {
				return Singleton4.getInstance();
			}
		};
		
		//创建线程池
		ExecutorService es = Executors.newFixedThreadPool(2);
		Future<Singleton4> f1 = es.submit(c);
		Future<Singleton4> f2 = es.submit(c);
		
		Singleton4 s1 = f1.get();
		Singleton4 s2 = f2.get();
		
		System.out.println(s1==s2);
		System.out.println(s1);
		System.out.println(s2);
		
		es.shutdown();
	}
}

同时,在Singleton4中的instance = new Singleton4();上方加上一个休眠时间

Thread.sleep(1000);//测试多线程下的线程安全问题

运行得到如下结果:
在这里插入图片描述
说明了线程存在安全问题,不适用于多线程,下面我们想办法将其修改处线程安全的

2.线程安全(适用于多线程)

只需要在上一步的Singleton4类中的getInstance()方法加上一个synchronized同步块即可

public class Singleton5 {
	private static Singleton5 instance;
	private Singleton5(){
		
	}
	
	public static Singleton5 getInstance(){
		synchronized (Singleton5.class) {
			if(instance == null){
				
				try {
					Thread.sleep(1000);//测试多线程下的线程安全问题
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
				instance = new Singleton5();
			}
		}
		
		return instance;
	}
}

然后用同样的测试方法测试,得到如下结果:
在这里插入图片描述
这样就保证了线程的安全问题

3.静态内部类形式(适用于多线程)

另外一种方法,更简便

//在内部类被加载和初始化时,才创建INCATANCE实例对象
//静态内部类不会自动随着外部类的加载和初始化而初始化,他是要单独去加载和初始化的。
//因为是在内部类加载和初始化时创建的,因此线程安全的
public class Singleton6 {
	private Singleton6(){
		
	}
	private static class Inner{
		public static final Singleton6 INCTANCE = new Singleton6();
	}
	
	public static Singleton6 getInstance(){
		return Inner.INCTANCE;
	}
}

测试方法同之前
测试结果:
在这里插入图片描述

觉得不错的小伙伴可以右上角点个赞或关注哟!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值