单例模式
1. 定义
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
- 1、单例类只能有一个实例。
- 2、单例类必须自己创建自己的唯一实例。
- 3、单例类必须给所有其他对象提供这一实例。
2. 具体实现
2.1 饿汉式(静态常量)
优点:在类加载的时候就完成了实例化,避免了线程同步的问题
缺点:没有达到lazy loading的效果,如果没有用到这个实例,就会造成内存的浪费
public class case1 {
private static final case1 single = new case1();
//私有化构造方法,不让外部能够创建
private case1(){
}
//通过调用静态方法获取实例
public static case1 getInstance(){
return single;
}
}
2.2 饿汉式(静态代码块)
优缺点和静态变量的类似
public class case2 {
private static case2 single;
//在静态代码块中创建并初始化单例对象,和静态变量类似,都是在类装载的时候初始化
static {
single = new case2();
}
//私有化构造方法,不让外部能够创建
private case2(){
}
//通过调用静态方法获取实例
public static case2 getInstance(){
return single;
}
}
2.3 懒汉式(线程不安全)
优点:懒加载
缺点:线程不安全,只能在单线程下使用
public class case3 {
private static case3 single;
//私有化构造方法
private case3(){
}
//获取实例
public static case3 getInstance(){
//只有当single为空的时候才创建,这里就是线程不安全的地方
//一个线程进入了if,还没来得及创建实例,此时,另一个线程来了,发现single是null于是又去创建了示例
if(single == null){
single = new case3();
}
return single;
}
}
2.4 懒汉式(线程安全 – synchronized)
优点:相比于第3个方法,确实是现场安全了
缺点:同时只有一个线程能够获取实例,效率大大的降低了
public class case4 {
private static case4 single;
//私有化构造方法
private case4(){
}
//获取实例,通过添加线程同步块来确保现场安全
//加上了synchronized之后同时只有一个线程能够调用getInstance,因此确保的线程的安全
public static synchronized case4 getInstance(){
if(single == null){
single = new case4();
}
return single;
}
}
2.5 懒汉式(同步代码块 – 线程不安全)
优点:不会像4一样每次都对方法加锁
缺点:线程不安全–我们无法保证if语句和synchronized语句块之间的这段时间是现场安全的
也就是说有可能一个线程进来发现single为空,进入if语句,此时因为一些原因,线程进入等待状态.之后另一个线程进来也发现single为空,这个线程成功的new的case5对象.之后,前一个线程结束等待,继续从if之后synchronized语句块之前执行,它也会去new一个case5对象,此时问题就发生了.
public class case5 {
private static case5 single;
//私有化构造方法
private case5(){
}
//获取实例,通过添加线程同步块来确保现场安全
public static case5 getInstance(){
//这种写法仍然不能保证线程安全
if(single == null){
//用类对象来上锁
synchronized(case5.class){
single = new case5();
}
}
return single;
}
}
2.6 懒汉式(双重检查)
优点:懒加载,且线程安全,效率也很不错
//volatile 千万别忘记 没有这个关键字仍然可能线程不安全
//它的作用是使CPU每次使用这个对象的时候不从缓存中取,而是直接从内存中取
//在赋值的时候也会及时的刷入内存
private static volatile case6 single;
//私有化构造方法
private case6(){
}
//获取实例,通过添加线程同步块来确保现场安全
public static case6 getInstance(){
//这种写法仍然不能保证线程安全
if(single == null){
//用类对象来上锁
synchronized(case6.class){
//再加一次检查,可以保证现场安全
if(single == null) {
single = new case6();
}
}
}
return single;
}
2.7 静态内部类
优点:懒加载,高效,线程安全,没有多余的检查逻辑和同步逻辑
public class case7 {
private case7(){
}
//静态内部类不随外部类的装载而装载,只有当我们第一次使用getInstance的时候,静态内部类才会装载
//类的装载是线程安全的,因此这样的写法也是线程安全的
private static class singleton{
private static final case7 single = new case7();
}
public static case7 getInstance(){
return singleton.single;
}
}
2.8 枚举
优点:线程安全的,同时也能防止反序列化重新创建新的对象
enum singleton{
INSTANCE; //属性
public void hello(){
System.out.println("hello");
}
}
3.总结
1.饿汉式都是线程安全的,它们或多或少都利用了Java的类加载机制,不过不能实现懒加载。
2.懒汉式可实现懒加载,但是有些实现方式要么不是线程安全的要么效率不高。
3.一般比较推荐使用后三种。