单例模式的五种实现方式
单例模式就是确保一个类只有一个实例,并提供全局的访问点。
1. 饿汉式
一上来就直接创建一个实例,不存在线程安全的问题。
package com.xqm.singleton;
public class Singleton1 {
private static Singleton1 instance = new Singleton1();
// 对外提供的静态方法
public static Singleton1 getSingleton(){
return instance;
}
}
class SingletonTest{
public static void main(String[] args) {
// 虽然创建了两个实例,但是都是指向同一个堆内存
Singleton1 singleton1=Singleton1.getSingleton();
Singleton1 singleton2=Singleton1.getSingleton();
}
}
2.懒汉式
懒汉式不像饿汉式,而是等到需要的时候再创建。
package com.xqm.singleton;
public class Singleton2 {
private static Singleton2 instance;
// 提供一个静态的方法,等到使用的时候再创建
public static synchronized Singleton2 getSingleton() {
if (instance == null) {
instance = new Singleton2();
}
return instance;
}
}
class Singleton2Test {
public static void main(String[] args) {
// 创建实例
Singleton2 singleton = Singleton2.getSingleton();
}
}
懒汉式存在线程安全问题,假如有两个线程Thread1,Thread2同时使用getSingleton,Thread1,Thread2同时到达if判断语句,这时会创建两个实例,造成线程不安全。因此可以给getSingleton上线程锁,但是会造成效率低。
3.双重校验
是在懒汉式上的改进。采用DCL的方式。
package com.xqm.singleton;
public class Singleton3 {
// volatile解决指令重排的问题,因为new instance是分为三步的
private volatile static Singleton3 instance;
public static Singleton3 getInstance(){
if (instance == null){
synchronized (Singleton3.class){
if (instance == null){
instance=new Singleton3();
}
}
}
return instance;
}
}
instance = new Singleton();
分为三步:
- 给instance分配内存;
- 调用 instance 的构造函数来初始化成员变量;
- 将给instance对象指向分配的内存空间(此时singleton才不为null)
4.静态内部类
package com.xqm.singleton;
public class Singleton4 {
// 静态内部类
private static class SingletonInstance {
private static final Singleton4 insatance = new Singleton4();
}
public static Singleton4 getInstance(){
return SingletonInstance.insatance;
}
}
5.枚举
枚举元素本身就是单例的,可以防止反射和反序列化。
package com.xqm.singleton;
public enum Singleton5 {
INSTANCE;
public void doSomething(){
System.out.println("自己操作");
}
}
class Singleton5Test{
public static void main(String[] args) {
// 直接通过这个方式调用
Singleton5.INSTANCE.doSomething();
}
}