参考:
https://design-patterns.readthedocs.io/zh_CN/latest/creational_patterns/singleton.html
对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。
单例模式可以使整个系统拥有一个全局的对象,方便资源的共享和系统行为的协调。
一:饿汉式
public class Singleton {
private static Singleton = new Singleton();
private Singleton() {}
public static getSignleton(){
return singleton;
}
}
饿汉式是最简单的,在类加载的时候就被创建,也没有多线程的问题,
但是不能满足延迟加载的需求
二:懒汉式:
public class Singleton {
//私有静态实例
private static Singleton singleton;
//构造私有
private Singleton() {}
public void doSomeThing() {
System.out.println("doSomeThing");
}
//加锁
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
懒汉式在用的的时候才考虑第一次创建对象,但是这里会有多线程的问题,多以用synchronized给方法加锁,
但是这里每次访问的时候都是synchronized的,性能会有影响,所以有了第三种
三:双重检查锁锁(double checked locking)
public class Singleton {
//私有静态实例
private static volatile Singleton singleton;
//构造私有
private Singleton() {}
public void doSomeThing() {
System.out.println("doSomeThing");
}
//加锁
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class){
if (singleton==null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
这里面解释一下:
1.为什么synchronized内部就还要再检查一次
因为在一个线程进入synchronized内部的时候,另外一个可能进了第一层if了,所以在锁起来的代码内部还要再判断一次
2.为什么要用volatile修饰singleton
volatile关键字可以保证有序性,如果不加这个关键字的话,可能一个线程在synchronized内部new 但还没执行完,另外一个线程得到的实例虽然不为null,但是并没有初始化
singleton = new Singleton() 这条命令执行分为三部分
1.为对象分配内存
2.执行构造方法语句,初始化实例对象
3.把singleton的引用指向分配的内存空间
如果指令重拍为132,则多线程情况下就会有问题!
-------------------------------------------------------------------------------------------------------------------------
上面三种方式是传统的单例模式实现方式
四:静态内部类
public class Singleton {
private Singleton() {}
public void doSomeThing() {
System.out.println("doSomeThing");
}
public static Singleton getInstance() {
return SingletonHolder.sInstance;
}
private static class SingletonHolder{
private static Singleton sInstance = new Singleton();
}
}
当外部类被加载时,其金泰内部类不会被加载,只有当调用SIngleton.getInstance()方法时,才会加载SingletonHolder并且初始化其成员变量。而类加载时是线程安全的。这样既保证了延迟加载,也保证了线程安全,同时也简化了代码量。
五 枚举单例
参考:
https://www.cnblogs.com/yangzhilong/p/6148639.html
http://blog.csdn.net/jeromeliee/article/details/79120373
好处:
1.实例的创建线程安全,确保单例
2.防止被反射创建多个实例
3.没有序列化的问题
/**
* 使用枚举的单例模式
*
* @author yzl
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class EnumSingleton{
private EnumSingleton(){}
public static EnumSingleton getInstance(){
return Singleton.INSTANCE.getInstance();
}
private static enum Singleton{
INSTANCE;
private EnumSingleton singleton;
//JVM会保证此方法绝对只调用一次
private Singleton(){
singleton = new EnumSingleton();
}
public EnumSingleton getInstance(){
return singleton;
}
}
}
public static void main(String[] args) {
EnumSingleton obj1 = EnumSingleton.getInstance();
EnumSingleton obj2 = EnumSingleton.getInstance();
//输出结果:obj1==obj2?true
System.out.println("obj1==obj2?" + (obj1==obj2));
}