单例模式singleton
保证一个类只有一个实例 并且提供一个访问该实例的全局访问点
主要
饿汉式( 线程安全调用效率高。但是,不能延时加载。) 懒汉式( 线程安全调用效率不高。但是,可以延时加载。) 其他 双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题。不建议使用) 静态内部类式(线程安全,调用效率高。 但是,可以延时加载) 枚举单例(线程安全,调用效率高,不能延时加载)
饿汉式
饿汉式 类加载到内存后,就实例化一个单例,JVM保证线程安全 简单实用,推荐实用! 唯一的缺点,不管用到与否,类装载时就完成实例化 (话说你不用,你装载它干啥) JVM保证每一个class只会load到内存一次,static也是保证class类load到内存后立马写到内存,所以也能保证一次
- 主体代码
public class Singleton01 {
private static final Singleton01 INSTANCE = new Singleton01();
private Singleton01() {
}
public static Singleton01 getInstance() {
return INSTANCE;
}
}
- 验证代码
public static void main(String[] args) {
Singleton01 m1 = Singleton01.getInstance();
Singleton01 m2 = Singleton01.getInstance();
System.out.println(m1 == m2);
}
懒汉式
- 主体代码
public class Singleton03 {
private static Singleton03 INSTANCE;
private Singleton03() {
}
public static Singleton03 getInstance() {
if (INSTANCE == null) {
//此处try-catch是测试多线程的代码
try {
Thread.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//当 INSTANCE为null的时候再初始化
INSTANCE = new Singleton03();
}
return INSTANCE;
}
}
- 验证代码
/**
* 线程不安全测试,hashcode不唯一
* @param args
*/
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()-> System.out.println(Singleton03.getInstance().hashCode())).start();
}
}
- 虽然优化了饿汉式的类用到在加载的需求,但是导致和更加严重的线程问题
懒汉式-加锁
可以通过synchronized解决,但也带来了效率下降
- 主体代码
public class Singleton03 {
private static Singleton03 INSTANCE;
private Singleton03() {
}
/**
* synchronized 加锁
* @return
*/
public static synchronized Singleton04 getInstance() {
if (INSTANCE == null) {
//此处try-catch是测试多线程的代码
try {
Thread.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//当 INSTANCE为null的时候再初始化
INSTANCE = new Singleton04();
}
return INSTANCE;
}
}
- 验证代码
/**
* 加锁后线程安全,但是效率降低
* @param args
*/
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()-> System.out.println(Singleton03.getInstance().hashCode())).start();
}
}
懒汉式继续优化
尝试通过减少同步代码块的方式提高效率,然而是不行的
public static Singleton05 getInstance() {
if (INSTANCE == null) {
//此处try-catch是测试多线程的代码
//尝试通过减少同步代码块的方式提高效率,然而是不行的
synchronized (Singleton05.class) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//当 INSTANCE为null的时候再初始化
INSTANCE = new Singleton05();
}
}
return INSTANCE;
}
为啥不行分析 等于没锁
懒汉式双重检查
- 主体代码
/**
* 此处需要加上volatile
* 涉及到指令重拍,会产生问题
*/
private static volatile Singleton06 INSTANCE;
private Singleton06() {
}
public static Singleton06 getInstance() {
if (INSTANCE == null) {
//此处try-catch是测试多线程的代码
synchronized (Singleton06.class) {
if (INSTANCE==null){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//当 INSTANCE为null的时候再初始化
INSTANCE = new Singleton06();
}
}
}
return INSTANCE;
}
懒汉式静态内部类-优秀写法
- 主体代码
public class Singleton07 {
private Singleton07() {
}
/**
* 你在初始化类的时候,加载Singleton07是不会加载Singleton07Holder的
* 加载的线程由JVM自身去保证
*/
private static class Singleton07Holder{
//因为构造方式是 private ,只有内部的类才可以new出来,外面的new不出来
private final static Singleton07 INSTANCE =new Singleton07();
}
/**
* 调用getInstance才会加载Singleton07Holder
* @return
*/
public static Singleton07 getInstance(){
return Singleton07Holder.INSTANCE;
}
}
- 验证代码
/**
*
* @param args
*/
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> System.out.println(Singleton07.getInstance().hashCode())).start();
}
}
懒汉式(Effective Java中)写到(完美)
- 主体代码
/**
* Effective Java
* Java之父编写的单例模式,懒汉式
* 不仅可以解决线程同步问题,还可以繁殖反序列化问题
*/
public enum Singleton08 {
//枚举类没有构造方法,所以不会被反序列化
INSTANCE;
}
- 验证代码
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() ->
System.out.println(Singleton08.INSTANCE.hashCode())
).start();
}
}
评论区