单例模式
概念
单例模式是一种创建型设计模式,它能够保证一个类只有一个实例,该单例对象必须由单例类自行创建,并提供一个访问该实例的全局节点。该方法可以创建一个新对象,但如果该对象已经被创建,则返回已有的对象。
例如,Windows中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。
单例模式结构

懒汉式单例
该模式的特点是类加载时没有生成单例,只有当第一次调用getlnstance方法时才去创建这个单例。实现时,仅需隐藏构造函数并实现一个静态的构建方法即可。
线程不安全
package singleton;
/**
* 懒汉式单例(线程不安全)
* private隐藏构造函数;static实现一个静态的构建方法
*/
public class LazySingletonUnsafe {
private static LazySingletonUnsafe instance = null;
public String value;
private LazySingletonUnsafe(String value) {
// The following code emulates slow initialization.
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.value = value;
}
public static LazySingletonUnsafe getInstance(String value) {
if (instance == null) {
instance = new LazySingletonUnsafe(value);
}
// 由于instance是static,所以当已存在时,不重复创建
return instance;
}
}
单线程测试代码
package singleton;
/**
* 使用单线程来测试懒汉单例模式
* @author chenzufeng
*/
public class SingleThread {
public static void main(String[] args) {
System.out.println("If you see the same value, then singleton was reused !" + "\n" +
"If you see different values, then 2 singletons were created !" + "\n" +
"RESULT :");
LazySingletonUnsafe lazySingletonUnsafe = LazySingletonUnsafe.getInstance("A");
LazySingletonUnsafe lazySingletonUnsafe1 = LazySingletonUnsafe.getInstance("B");
System.out.println(lazySingletonUnsafe.value);
System.out.println(lazySingletonUnsafe1.value);
}
}
输出结果:
If you see the same value, then singleton was reused !
If you see different values, then 2 singletons were created !
RESULT :
A
A
多线程测试代码
package singleton;
/**
* 多线程来测试懒汉单例模式(线程不安全)
* @author chenzufeng
* @date 2021-2-8
*/
public class MultiThread {
public static void main(String[] args) {
System.out.println("If you see the same value, then singleton was reused !" + "\n" +
"If you see different values, then 2 singletons were created !" + "\n" +
"RESULT :");
Thread threadA = new Thread(new ThreadA());
Thread threadB = new Thread(new ThreadB());
threadA.start();
threadB.start();
}
static class ThreadA implements Runnable {
@Override
public void run() {
LazySingletonUnsafe lazySingletonUnsafe = LazySingletonUnsafe.getInstance("A");
System.out.println(lazySingletonUnsafe.value);
}
}
static class ThreadB implements Runnable {
@Override
public void run() {
LazySingletonUnsafe lazySingletonUnsafe = LazySingletonUnsafe.getInstance("B");
System.out.println(lazySingletonUnsafe.value);
}
}
}
输出结果,有可能没有体现出单例:
If you see the same value, then singleton was reused !
If you see different values, then 2 singletons were created !
RESULT :
A
B
线程安全:双检锁
package singleton;
/**
* 懒汉式单例(线程安全)
* private隐藏构造函数;static实现一个静态的构建方法
* @author chenzufeng
* @date 2021-2-8
*/
public class LazySingletonSafe {
private static volatile LazySingletonSafe instance;
public String value;
private LazySingletonSafe(String value) {
this.value = value;
}
public static LazySingletonSafe getInstance(String value) {
/**
* The approach taken here is called double-checked locking (DCL).
* It exists to prevent race condition between multiple threads that may
* attempt to get singleton instance at the same time, creating separate instances as a result.
*
* It may seem that having the `result` variable here is completely pointless.
* There is, however, a very important caveat when implementing double-checked locking in Java,
* which is solved by introducing this local variable.
*
*/
LazySingletonSafe result = instance;
if (result != null) {
return result;
}
synchronized (LazySingletonSafe.class) {
if (instance == null) {
instance = new LazySingletonSafe(value);
}
return instance;
}
}
}
测试代码:
package singleton;
/**
* 多线程来测试懒汉单例模式(线程安全)
*/
public class TestLazySingletonSafe {
public static void main(String[] args) {
System.out.println("If you see the same value, then singleton was reused !" + "\n" +
"If you see different values, then 2 singletons were created !" + "\n" +
"RESULT :");
new Thread(
() -> {
LazySingletonSafe lazySingletonSafe = LazySingletonSafe.getInstance("A");
System.out.println(lazySingletonSafe.value);
}
).start();
new Thread(
() -> {
LazySingletonSafe lazySingletonSafe = LazySingletonSafe.getInstance("B");
System.out.println(lazySingletonSafe.value);
}
).start();
}
}
输出结果:
If you see the same value, then singleton was reused !
If you see different values, then 2 singletons were created !
RESULT :
A
A
采用双锁机制,安全且在多线程情况下能保持高性能。
饿汉式单例
该模式的特点是类一旦加载就创建一个单例,保证在调用getInstance方法之前单例已经存在了:
package singleton;
public class HungrySingleton {
private static final HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {}
public static HungrySingleton getInstance() {
return instance;
}
}
没有加锁,执行效率会提高;但类加载时就初始化,浪费内存。