单例模式
2021-05-05 本文已影响0人
金煜博
什么是单例模式?
保证jvm中只能有一个实例
1.饿汉式
提前创建对象,保证了线程的安全,占内存空间,影响程序启动效率
示例代码
public class Singleton {
public static Singleton singleton = new Singleton();
private Singleton() {
}
public static Singleton getInitial(){
return singleton;
}
}
public class V1 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInitial();
Singleton singleton2 = Singleton.getInitial();
System.out.println(singleton1==singleton2);
}
}
2.懒汉式
需要用到时才创建对象实例,因为在方法上加了锁,导致创建和获取对象都上了锁,效率低。
示例代码
public class Singleton {
private static Singleton singleton;
//私有的构造方法可以禁止new对象
private Singleton() {
}
//在方法上加锁导致读写操作都上锁了效率低,成了单线程了
public static synchronized Singleton getInitial(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
}
public class V2 {
//创建100个线程模拟执行效率和线程是否安全
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
Singleton singleton1 = Singleton.getInitial();
System.out.println(Thread.currentThread().getName()+"-----------"+singleton1);
}
}).start();
}
}
}
3.双重检验锁
双重检验锁解决了在写上锁,读不上锁,有效的提高执行效率
(重点理解下两个if判断的意义)
示例代码
public class Singleton {
private static Singleton singleton;
//私有的构造方法可以禁止new对象
private Singleton() {
}
//在代码段上加锁,只是在创建Singleton时上锁,读取时无锁。(双重检验锁)
public static Singleton getInitial() {
//第一个if判断singleton是否null不是就可以直接读取singleton对象
if (singleton == null) {
synchronized (Singleton.class) {
//第二个if判断防止多个线程同时拿到锁后,重复创建singleton对象
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
public class V2 {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
Singleton singleton1 = Singleton.getInitial();
System.out.println(Thread.currentThread().getName()+"-----------"+singleton1);
}
}).start();
}
}
}
4.静态类部内
静态内部内结合了懒汉式和饿汉式的优,效率高
示例代码
public class Singleton {
private Singleton() {
System.out.println("-----------初始化构造函数-------");
}
private static Singleton initialSingleton(){
return GetSingleton.singleton;
}
private static class GetSingleton{
private final static Singleton singleton = new Singleton();
}
public static void main(String[] args) {
System.out.println("开始运行");
Singleton singleton1 = Singleton.initialSingleton();
Singleton singleton2 = Singleton.initialSingleton();
System.out.println(singleton1==singleton2);
}
5.枚举
使用枚举方式可以有效的防止反射机制和序列化,因为jdk底层源码中有if判断禁用了反射与序列化
public enum Singleton {
SL;
public void show(){
System.out.println("枚举单例模式可以防止反射和序列化");
}
}
public class V4 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.SL;
Singleton singleton2 = Singleton.SL;
System.out.println(singleton1 == singleton2);
singleton1.show();
}
}
6.单例模式额外注意点
1.使用反射机制可以破解单例
2.使用序列化可以破解单例
3.可以在无参构造方法中判断单例对象是否为null,是就抛出异常
4.不用懒加载的话用饿汉式,反之用静态类部内方式,需要序列化的对象用枚举方式