本文章仅为学习和记录之用,如有侵权,请联系删除
单例设计模式
1. 什么是Singleton
Singleton:在Java中指单例模式
单例设计模式:即某个类在整个系统中只能有一个实例对象可被获取和使用
2. 设计要点
- 构造器私有化
保证在外部无法随意创建该类实例 - 含有一个静态变量保存唯一实例
因为外部无法创建实例,所以只能在内部创建并保存 - 对外暴露该实例
3.1 直接暴露,使用public修饰
3.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());
}
}