文章目录
1.单例模式基本介绍
1.介绍
2.单例模式八种方式
2.饿汉式(静态常量,推荐)
1.基本步骤
1.构造器私有化(防止new)
2.类的内部创建对象
3.向外暴露一个静态的公共方法
2.代码实现
java
package com.sun.type1;
/**
* Description: 单例模式饿汉式(静态变量)
* @Author sun
* @Create 2024/5/26 19:28
* @Version 1.0
*/
public class Singleton01 {
public static void main(String[] args) {
// 通过公有的静态方法,获取实例
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance1 == instance); // 返回的是true
}
}
class Singleton {
// 1.构造器私有化
private Singleton() {}
// 2.本类的内部创建对象实例
private final static Singleton instance = new Singleton();
// 3.暴露一个公有的静态方法,返回实例对象
public static Singleton getInstance() {
return instance;
}
}
3.优缺点分析
3.饿汉式(静态代码块,推荐)
1.代码实现
java
package com.sun.type1;
/**
* Description: 单例模式饿汉式(静态代码块)
* @Author sun
* @Create 2024/5/26 19:49
* @Version 1.0
*/
public class Singleton02Test {
public static void main(String[] args) {
// 通过公有的静态方法,获取实例
Singleton02 instance = Singleton02.getInstance();
Singleton02 instance1 = Singleton02.getInstance();
System.out.println(instance1 == instance); // 返回的是true
}
}
class Singleton02 {
// 1.构造器私有化
private Singleton02() {}
// 2.本类的内部有一个静态属性
private static Singleton02 instance;
// 3.使用静态代码块为静态属性初始化
static {
instance = new Singleton02();
}
// 4.暴露一个公有的静态方法,返回实例对象
public static Singleton02 getInstance() {
return instance;
}
}
2.优缺点分析
4.懒汉式(线程不安全,实际开发不要使用)
1.代码实现
java
package com.sun.type3;
/**
* Description: 单例模式懒汉式(线程不安全)
* @Author sun
* @Create 2024/5/26 20:02
* @Version 1.0
*/
public class Singleton03Test {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance1 == instance);
}
}
class Singleton {
// 1.构造器私有化
private Singleton() {}
// 2.静态属性,存放本类对象
private static Singleton instance;
// 3.静态的公有方法,当使用到该方法时才会去创建instance
public static Singleton getInstance() {
// 当静态属性没有被赋值的时候再去创建
if (instance == null) {
instance = new Singleton();
}
// 如果这个静态属性被初始化过了,就直接返回,保证是单例的
return instance;
}
}
2.优缺点分析
5.懒汉式(线程安全,同步方法,实际开发不推荐使用)
1.代码实现
java
package com.sun.type4;
/**
* Description: 单例模式懒汉式(线程安全,同步方法)
* @Author sun
* @Create 2024/5/26 20:15
* @Version 1.0
*/
public class Singleton04Test {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance1 == instance);
}
}
class Singleton {
// 1.构造器私有化
private Singleton() {}
// 2.静态属性,存放本类对象
private static Singleton instance;
// 3.静态的公有方法,当使用到该方法时才会去创建instance,使用synchronized实现线程安全
public static synchronized Singleton getInstance() {
// 当静态属性没有被赋值的时候再去创建
if (instance == null) {
instance = new Singleton();
}
// 如果这个静态属性被初始化过了,就直接返回,保证是单例的
return instance;
}
}
2.优缺点分析
6.懒汉式(线程安全,同步代码块,解决不了同步问题)
1.介绍
2.优缺点分析
7.双重检查(推荐使用)
1.代码实现
java
package com.sun.type6;
/**
* Description: 双重检查
* @Author sun
* @Create 2024/5/26 20:15
* @Version 1.0
*/
public class Singleton06Test {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance1 == instance);
}
}
class Singleton {
// 1.构造器私有化
private Singleton() {}
// 2.静态属性,存放本类对象,使用volatile防止指令重排序
private static volatile Singleton instance;
// 3.静态的公有方法,当使用到该方法时才会去创建instance
public static Singleton getInstance() {
// 先检查一次静态属性是否为空,这样可以过滤掉一部分
if (instance == null) {
// 如果为空,则进入同步代码块创建对象,由于还是会有多个线程进来,所以在内部需要二次检查
synchronized (Singleton.class) {
// 在此完成双重检查
if (instance == null) {
instance = new Singleton();
}
}
}
// 如果这个静态属性被初始化过了,就直接返回,保证是单例的
return instance;
}
}
2.关于volatile关键字
使用 volatile
的常见场景
-
直接赋值:
- 设置标志位,如控制线程的开始或停止。
- 单个配置值的更新,这个值被多个线程读取。
例如,一个线程控制开关:
javapublic class TaskRunner implements Runnable { private volatile boolean stop = false; public void run() { while (!stop) { // 执行任务 } } public void stop() { stop = true; } }
-
单一读取操作:
- 在这种情况下,
volatile
保证了每次读取操作都从主内存中获取最新值,而不是从线程的本地缓存。
- 在这种情况下,
复合操作的限制
如果操作涉及到对变量的读取、修改和写回(如 a++
),则 volatile
不能保证操作的原子性。这类操作需要使用 synchronized
或 java.util.concurrent.atomic
包中的原子类。例如,对一个计数器进行增加操作:
java
public class Counter {
private volatile int count = 0; // 这是错误的做法,对于 count++ 操作
public void increment() {
count++; // 这不是线程安全的
}
}
在上面的代码中,count++
操作实际上是三个步骤:读取 count
的当前值,增加 1,然后写回新值。这三个步骤不是原子操作,因此即使 count
被声明为 volatile
,在多线程环境中也可能导致不正确的结果。
总结
总之,当你的需求仅限于确保多线程之间对单个变量直接赋值操作的可见性和有序性时,使用 volatile
是合适的。然而,对于需要多个步骤或基于当前值的修改的操作,你需要使用更强的同步机制。
3.优点分析
8.静态内部类实现(推荐使用)
1.前置知识
1.外部类被装载,静态内部类不会被装载
2.当调用静态内部类的属性时,静态内部类会被装载且只会被装载一次
3.在类装载的时候是线程安全的
2.代码实现
java
package com.sun.type7;
/**
* Description: 单例模式(静态内部类实现)
* @Author sun
* @Create 2024/5/26 20:51
* @Version 1.0
*/
public class Singleton07Test {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance1 == instance);
}
}
class Singleton {
// 1.构造器私有化
private Singleton() {}
// 2.一个静态内部类,该类有一个静态属性,存放外部类的对象
private static class SingletonInstance {
public static final Singleton INSTANCE = new Singleton();
}
// 3.静态的公有方法,当要使用时调用内部类的静态属性,此时静态内部类会被装载,且只会被装载一次
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
3.优点分析
9.枚举实现(推荐使用)
1.代码实现
java
package com.sun.type8;
/**
* Description: 单例模式(静态内部类实现)
* @Author sun
* @Create 2024/5/26 20:51
* @Version 1.0
*/
public class Singleton08Test {
public static void main(String[] args) {
Singleton instance = Singleton.INSTANCE;
Singleton instance2 = Singleton.INSTANCE;
System.out.println(instance2 == instance);
}
}
enum Singleton {
// 这个就相当于调用无参构造创建了这个类的一个对象,可以实现单例模式
INSTANCE;
}