一、单例模式的特点:
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
二、常见的几种单例模式
1、懒汉式,线程不安全
- 单线程下,只会创建一个实例对象。多线程下会创建多个实例对象。
public class SignletonDemo {
//声明类的唯一实例
private static SignletonDemo instance;
//构造方法私有化,不允许外部直接创建对象
private SignletonDemo() {
System.out.println(Thread.currentThread().getName()+" 构造方法SingletonDemo");
}
public static SignletonDemo getInstance() {
if(instance == null){
instance = new SignletonDemo();
}
return instance;
}
public static void main(String[] args) {
// System.out.println(SignletonDemo.getInstance() == SignletonDemo.getInstance());
// System.out.println(SignletonDemo.getInstance() == SignletonDemo.getInstance());
// System.out.println(SignletonDemo.getInstance() == SignletonDemo.getInstance());
for (int i = 0; i < 10; i++) {
new Thread(() -> {
SignletonDemo.getInstance();
}, String.valueOf(i)).start();
}
}
}
2、懒汉式,线程安全(加synchronized)
- 问题:不高效,任何时候只能有一个线程调用 getSingleton()
public class SignletonDemo {
private static SignletonDemo instance;
private SignletonDemo() {
}
public static synchronized SignletonDemo getSingleton() {
if (instance == null) {
instance = new SignletonDemo();
}
return instance;
}
}
3、DCL双重检锁机制(Double Check Lock)
- 两次检查instance == null,一次是在同步块外,一次是在同步块内。第一次检查instance不为null,那么就不需要执行下面的加锁和初始化操作。可以大幅降低synchronized带来的性能开销。
- 使用volatile禁止指令重排序,对volatile变量的写操作都先行发生于后面对这个变量的读操作
public class SignletonDemo {
private static volatile SignletonDemo instance;
private SignletonDemo() {
System.out.println(Thread.currentThread().getName() + " 构造方法SingletonDemo");
}
public static SignletonDemo getInstance() {
if (instance == null) {
synchronized (SignletonDemo.class) {
if (instance == null) {
instance = new SignletonDemo();
}
}
}
return instance;
}
public static void main(String[] args) {
// System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
// System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
// System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
for (int i = 0; i < 10; i++) {
new Thread(() -> {
SingletonDemo.getInstance();
}, String.valueOf(i)).start();
}
}
}
5、饿汉式
- 基于classloder机制避免了多线程的同步问题,不过instance在类装载时就实例化,这时候初始化instance没有达到lazy loading的效果
public class SignletonDemo {
//类加载时就初始化
private static final SignletonDemo instance = new SignletonDemo();
private SignletonDemo() {
}
public static SignletonDemo getInstance() {
return instance;
}
}
5、静态内部类
- 利用了classloder的机制来保证初始化instance时只有一个线程。
- SignletonDemo类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance
public class SignletonDemo {
private static class SingletonHolder {
private static final SignletonDemo instance = new SignletonDemo();
}
private SignletonDemo() {
}
public static final SignletonDemo getInstance() {
return SingletonHolder.instance;
}
}
6、枚举
- 不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象
public class SignletonDemo {
public enum EasySingleton{
INSTANCE;
}
}
总结:
一般情况下直接使用饿汉式就好了,
如果明确要求要懒加载(lazy initialization)会倾向于使用静态内部类,
如果涉及到反序列化创建对象时会试着使用枚举的方式来实现单例
公共静态void main (String [] args) { / / System.out.println (SingletonDemo.getInstance () = = SingletonDemo.getInstance ()); / / System.out.println (SingletonDemo.getInstance () = = SingletonDemo.getInstance ()); / / System.out.println (SingletonDemo.getInstance () = = SingletonDemo.getInstance ()); for (int i = 0;我< 10;我+ +){ 新线程(()- > { SingletonDemo.getInstance (); },String.valueOf(我).start (); }
基本翻译
n. 实例;情况;建议
vt. 举...为例