为什么要设计单例模式?
当我们想要整个应用只存在一个实例时,我们就需要设计单例模式。例如:计数器,整个应用中如果存在多个计数器实例,那么将会产生混乱。还有像Servlet中的应用上下文也必须使用同一个对象,这就要求必须是单例。
单例模式的实现方式有哪些?
大体上可以分为两种:饿汉式和懒汉式
饿汉式
饿汉式,其实很好理解,就是在类加载的时候就实例化。
方式一:静态代码块
当我们想到类加载时完成实例化操作,那么我们很容易想到static关键字,没错我们就可以使用静态代码块来实例化。
具体代码如下:
public class Demo01 {
private static Demo01 instance;
private Demo01() {
}
static {
instance = new Demo01();
}
// 对外提供一个public的方法来获取实例
public static Demo01 getInstance() {
return instance;
}
public static void main(String[] args) {
Demo01 instance = Demo01.getInstance();
Demo01 instance1 = Demo01.getInstance();
System.out.println(instance == instance1);
System.out.println(instance.hashCode());
System.out.println(instance1.hashCode());
}
}
运行结果:
true
1265094477
1265094477
方式二:静态变量
我们还可以使用静态变量方式来实例化。
具体代码如下:
public class Demo02 {
private static Demo02 instance = new Demo02();
private Demo02() {
}
// 对外提供一个public方法来获取实例
public static Demo02 getInstance() {
return instance;
}
public static void main(String[] args) {
Demo02 instance = Demo02.getInstance();
Demo02 instance1 = Demo02.getInstance();
System.out.println(instance == instance1);
System.out.println(instance.hashCode());
System.out.println(instance1.hashCode());
}
}
运行结果:
true
1265094477
1265094477
懒汉式
懒汉式表示在需要第一次需要创建实例时才会创建。它是线程不安全的。
方式一:线程不安全
具体实现如下:
class Demo03 {
private static Demo03 instance;
private Demo03() {
}
// 对外提供一个获取实例的方法
public static Demo03 getInstance() {
// 判断该实例是否已经存在了
// 如果存在了,就直接返回当前实例
if (instance == null) {
instance = new Demo03();
}
return instance;
}
}
class Main {
public static void main(String[] args) {
Demo03 instance = Demo03.getInstance();
Demo03 instance1 = Demo03.getInstance();
System.out.println(instance == instance1);
System.out.println(instance.hashCode());
System.out.println(instance1.hashCode());
}
}
运行结果:
true
1265094477
1265094477
但是在多线程环境下,它可能会创建多个实例。
例如:
class Demo03 {
private static Demo03 instance;
private Demo03() {
}
// 对外提供一个获取实例的方法
public static Demo03 getInstance() {
// 判断该实例是否已经存在了
// 如果存在了,就直接返回当前实例
if (instance == null) {
instance = new Demo03();
}
return instance;
}
}
class Thread01 extends Thread {
@Override
public void run() {
Demo03 instance = Demo03.getInstance();
System.out.println(instance);
}
}
class Thread02 extends Thread {
@Override
public void run() {
Demo03 instance = Demo03.getInstance();
System.out.println(instance);
}
}
class Main {
public static void main(String[] args) {
Thread01 thread01 = new Thread01();
Thread02 thread02 = new Thread02();
thread01.start();
thread02.start();
}
}
运行结果:
top.lukeewin.basics.demo07.Demo03@5f1db1c2
top.lukeewin.basics.demo07.Demo03@ddbeecd
方式二:线程安全,加了同步锁
解决办法:使用synchronized同步锁。
具体代码如下:
class Demo03 {
private static Demo03 instance;
private Demo03() {
}
// 对外提供一个获取实例的方法
public static synchronized Demo03 getInstance() {
// 判断该实例是否已经存在了
// 如果存在了,就直接返回当前实例
if (instance == null) {
instance = new Demo03();
}
return instance;
}
}
方式三:双重检查法,线程安全
还可以进一步优化,使用双重检查法。
具体代码如下:
class Demo03 {
private static Demo03 instance;
private Demo03() {
}
// 对外提供一个获取实例的方法
public static Demo03 getInstance() {
// 判断该实例是否已经存在了
// 如果存在了,就直接返回当前实例
if (instance == null) {
synchronized (Demo03.class) {
if (instance == null) {
instance = new Demo03();
}
}
}
return instance;
}
}
方式四:静态内部类
此外,还可以使用静态内部类来创建单例。
具体代码如下:
public class Singleton01 {
private Singleton01() {
}
private static class Single {
private static final Singleton01 instance = new Singleton01();
}
public static Singleton01 getInstance() {
return Single.instance;
}
}
class Main03 {
public static void main(String[] args) {
Singleton01 instance = Singleton01.getInstance();
Singleton01 instance1 = Singleton01.getInstance();
System.out.println(instance == instance1);
System.out.println(instance.hashCode());
System.out.println(instance1.hashCode());
}
}
方式五:枚举
我们还可以使用枚举类型来创建单例。
具体代码如下:
public enum Singleton {
INSTANCE;
}
class Main02 {
public static void main(String[] args) {
Singleton instance = Singleton.INSTANCE;
Singleton instance1 = Singleton.INSTANCE;
System.out.println(instance == instance1);
}
}
方式六:使用Map集合
我们可以使用Map集合来实现。
具体代码如下:
public class SingletonManager {
// 定义一个静态Map集合
private static Map<String, Object> objectMap = new HashMap<>();
private SingletonManager() {
}
public static void registerSingleton(String singletonName, Object instance) {
if (!objectMap.containsKey(singletonName)) {
objectMap.put(singletonName, instance);
}
}
public static Object getInstance(String singletonName) {
return objectMap.get(singletonName);
}
}
class Main04 {
public static void main(String[] args) {
SingletonManager.registerSingleton("bean01", new Student("张三", 18));
Object bean01 = SingletonManager.getInstance("bean01");
Object bean02 = SingletonManager.getInstance("bean01");
System.out.println(bean01 == bean02);
System.out.println(bean01.hashCode());
System.out.println(bean02.hashCode());
}
}
Q.E.D.