2.单例设计模式

本文章仅为学习和记录之用,如有侵权,请联系删除

1. 什么是Singleton

Singleton:在Java中指单例模式

单例设计模式:即某个类在整个系统中只能有一个实例对象可被获取和使用

2. 设计要点

  1. 构造器私有化
    保证在外部无法随意创建该类实例
  2. 含有一个静态变量保存唯一实例
    因为外部无法创建实例,所以只能在内部创建并保存
  3. 对外暴露该实例
    3.1 直接暴露,使用public修饰
    3.2 使用静态方法获取

3. 常见形式

饿汉式(饥汉式)

  1. 直接实例化(简洁直观)
  2. 枚举式(最简洁)
  3. 静态代码块(适合复杂实例化)

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

问题:无论是否需要这个对象都会创建,造成资源浪费

1. 直接实例化(简洁直观)

public class Singleton {

	/**
	 * 该函数限制用户主动创建实例
	 */
	private Singleton() {}

	private static final Singleton INSTANCE = new Singleton();

	/**
	 * 获取Singleton实例,也叫静态工厂方法
	 * @return Singleton
	 */
	public static Singleton getInstance() {
		return INSTANCE;
	}

}

2. 枚举式(最简洁)

public enum Singleton2 {
	// 枚举类会自动为变量初始化
	INSTANCE
}

3. 静态代码块(适合复杂实例化)

例如:加载配置文件对类中的其他变量进行初始化操作

public class Singleton3 {

	public static final Singleton3 INSTANCE;
	
	// 需要初始化的变量
	private String info;
	
	static {
		try {
			Properties pro = new Properties();
			// 使用类加载器加载配置文件, demo.properties在根目录(src)下
			InputStream resourceAsStream = Singleton3.class.getClassLoader().getResourceAsStream("demo.properties");
			pro.load(resourceAsStream);
			
			INSTANCE = new Singleton3(pro.getProperty("info"));
		} catch (IOException e) {
			// 不会中断程序运行
			throw new RuntimeException(e);
		}
	}
	
	private Singleton3(String info) {
		this.info = info;
	}

	public String getInfo() {
		return info;
	}

	public void setInfo(String info) {
		this.info = info;
	}

	@Override
	public String toString() {
		return "Singleton3 [info=" + info + "]";
	}
}

懒汉式(饱汉式)

延迟创建对象

1. 普通懒汉单例模式

问题: 该模式只适用于单线程模式,在多线程时,可能会出现多个线程不是使用同一个对象的问题,也就不是单例模式了

public class Singleton {
	
	/**
	 * 该函数限制用户主动创建实例
	 */
	private Singleton() {}
	private static Singleton singleton = null;
	
	/**
	 * 获取Singleton实例(也叫静态工厂方法)
	 * @return Singleton
	 */
	public static Singleton getSingleton() {
		/* 当singleton为空时创建它,反之直接返回,保证唯一性 */
		if(singleton == null){
			singleton = new Singleton();
		}
		return singleton;
	}
}

示例:

if(singleton == null){
	Thread.sleep(1000); // 休眠一秒
	singleton = new Singleton();
}

假设有两个线程,当第一个线程执行到“Thread.sleep(1000);”时会休眠一秒,此时singleton仍然为null,所以当第二个线程进行 if 判断时,仍然为true,然后同样执行到“Thread.sleep(1000);”时会休眠一秒,当一个线程休眠结束后会向下继续执行,创建一个对象,而第二个线程休眠结束时,也会继续向下执行,又重新创建了一个对象,此时,就有了两个该类对象实例,已经违反了单例设计模式

测试示例:
public static void main(String[] args) throws InterruptedException, ExecutionException {
		// 通过匿名内部类的方式创建线程对象,也可以使用Runnable或者Thread类
		Callable<Singleton4> a = new Callable<Singleton4>() {

			@Override
			public Singleton4 call() throws Exception {
			 	// System.out.println("Callable: " + Singleton4.getInstance());
				return Singleton4.getInstance();
			}
		};
		
		// 创建一个线程池,包含两个线程
		ExecutorService ex = Executors.newFixedThreadPool(2);
		Future<Singleton4> s1 = ex.submit(a);
		Future<Singleton4> s2 = ex.submit(a);
		
		System.out.println("Callable: " + s1.get());
		System.out.println("Callable: " + s2.get());
		
		// 关闭线程池
		ex.shutdown();
}

输出:
Callable: singleton.Singleton4@33909752
Callable: singleton.Singleton4@55f96302

注意: 并不是每一次运行时都创建两个对象,有时也是一个对象

Runnable测试示例
// Runnable示例
Runnable r = new Runnable() {
	
	@Override
	public void run() {
		Singleton4 instance = Singleton4.getInstance();
		System.out.println("Runnable: " + instance);
	}
};

ExecutorService es2 = Executors.newFixedThreadPool(2);
es2.submit(r);
es2.submit(r);

es2.shutdown();

2. 线程安全的懒汉单例模式

  • 在getSingleton()添加synchronized同步
public class Singleton {
	
/**
	 * 该函数限制用户主动创建实例
	 */
	private Singleton() {}
	private static Singleton singleton = null;
	/**
	 * 获取Singleton实例,也叫静态工厂方法
	 * @return Singleton
	 */
	public static synchronized Singleton getSingleton(){
		if(singleton==null){
			singleton=new Singleton();
		}
		return singleton;
	}
	
}
  • 使用synchronized同步代码块
    双重检查锁定,使用同步代码块解决线程安全问题,同时在同步代码块外面添加判断,防止每次线程进来都要获取同步锁导致线程阻塞
public class Singleton {

	/**
	 * 该函数限制用户主动创建实例
	 */
	private Singleton() {}

	private volatile static Singleton singleton = null;

	/**
	 * 获取Singleton实例,也叫静态工厂方法
	 * @return Singleton
	 */
	public static Singleton getInstance() {
		if (singleton == null) {
			synchronized (Singleton.class) {
				if (singleton == null) {
					singleton = new Singleton();
				}
			}
		}
		return singleton;
	}

}

3. 静态内部类

静态内部类比双重检查锁定和在getInstance()方法上加同步都要好,实现了线程安全又避免了同步带来的性能影响

public class Singleton {

	/**
	 * 静态内部类
	 * @author kimball
	 *
	 */
	private static class LazyHolder {
		// 创建Singleton实例
		private static final Singleton INSTANCE = new Singleton();
	}

	/**
	 * 该函数限制用户主动创建实例
	 */
	private Singleton() {}

	/**
	 * 获取Singleton实例,也叫静态工厂方法
	 * @return Singleton
	 */
	public static final Singleton getInstance() {
		return LazyHolder.INSTANCE;
	}
}

在内部类被加载和初始化时,才创建INSTANCE对象
静态内部类不会自动随着外部类的加载和初始化而初始化,他是需要单独加载和初始化的
因为是在内部类加载和初始化时创建的,因此时线程安全的
java机制规定:类只有在被使用时才会被加载(相当于延迟加载),而且其加载过程是线程安全的(内部实现)

登记单利模式

public class Singleton {

	// 存储需要进行维护和管理的类的实例
	private static Map<String, Singleton> map = new HashMap<String, Singleton>();
	
	/**
	 * 静态创建实例并添加到Map集合
	 */
	static {
		Singleton singleton = new Singleton();
		map.put(singleton.getClass().getName(), singleton);
	}

	/**
	 * 该函数限制用户主动创建实例
	 */
	private Singleton() {};

	/**
	 * 获取Singleton实例,也叫静态工厂方法
	 * @return Singleton
	 */
	public static Singleton getInstance(String name) {
		/* 根据指定的类的名称从mao中获取实例并返回 */
		return map.get(name);
	}

	// 一个示例性的商业方法
	public String about() {
		return "你好,我是RegSingleton";
	}

	public static void main(String[] args) {
		Singleton singleton = Singleton.getInstance("com.Singleton");
		System.out.println(singleton.about());
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值