单例模式的作用1.用于单个实例存储公共数据,2.节省内存;
spring中默认的bean作用域就是单例;
jdk的Runtime类就是典型的单例饿汉式实现:
实现下来,大概有4种实现方式:
- 饿汉:jvm启动之后就初始化了一个实例,线程安全,但不用的话有点内存浪费;
- 懒汉:用到才初始化,需要考虑并发情况下的线程安全;
- 内部类:利用内部类用到才初始化的特性;
- 枚举:天然的单例;
注:饿汉和懒汉实现细节上还有其他不同细节实现
UML类图:
代码实现:
package com.xiaomu.arithmetic.design.singleton;
/**
* 饿汉模式-属性初始化实例
* jvm启动之后就初始化了一个实例,线程安全,但不用的话有点内存浪费
*/
class Badmash01 {
//构造器私有,防止外部创建实例
private Badmash01() {
}
//属性初始化
private static final Badmash01 instance = new Badmash01();
public static Badmash01 getInstance() {
return instance;
}
}
/**
* 饿汉模式-静态代码块初始化实例
* jvm启动之后就初始化了一个实例,线程安全,但不用的话有点内存浪费
*/
class Badmash02 {
//构造器私有,防止外部创建实例
private Badmash02() {
}
private static Badmash02 instance;
//静态代码块初始化实例
static {
instance = new Badmash02();
}
public static Badmash02 getInstance() {
return instance;
}
}
/**
* 懒汉式-线程不安全
* 用到才创建,线程不安全,并发情况会出现多个实例
*/
class Idler01 {
private static Idler01 instance;
//构造器私有,防止外部创建实例
private Idler01() {
}
public static Idler01 getInstance() {
if (instance == null) {
//并发情况,多个线程进到这里则会获取到不同的实例!
instance = new Idler01();
}
return instance;
}
}
/**
* 懒汉式-双重检查锁
* 用到才创建,线程安全,既不会在初始就创建实例,又能保证并发情况只有一个实例
* volatile关键字jdk1.5之后才有
*/
class Idler02 {
//volatile修饰,保证可见性和指令重排
private static volatile Idler02 instance;
//构造器私有,防止外部创建实例
private Idler02() {
}
public static Idler02 getInstance() {
//第1次检查,判断实例已经有了就返回
if (instance == null) {
//并发情况,多个线程可能会来到这里,打个标记a
//synchronized同步代码块,锁对象为Singleton004的类对象,保证只有1个线程进到里面
synchronized (Idler02.class) {
//第2次检查volatile修饰的instance,判断实例是否空
if (instance == null) {
instance = new Idler02();
}
}
}
return instance;
}
}
/**
* 内部类
* 线程安全,利用内部类用到才初始化的特性
*/
class OutClass {
private static OutClass instance;
//构造器私有,防止外部创建实例
private OutClass() {
}
//内部类,用到了才初始化
private static class Inner {
private static OutClass instance = new OutClass();
}
public static OutClass getInstance() {
return Inner.instance;
}
}
/**
* 枚举
*/
enum Enum {
//枚举实例
ONE;
public static Enum getInstance() {
return ONE;
}
}
/**
* @author ltx
* 单例模式
*/
public class SingletonTest {
public static void main(String[] args) {
//饿汉-属性初始化
Badmash01 instance1 = Badmash01.getInstance();
Badmash01 instance2 = Badmash01.getInstance();
System.out.printf("饿汉-属性初始化: %s\n", instance1 == instance2);
//饿汉-静态代码块初始化
Badmash02 instance3 = Badmash02.getInstance();
Badmash02 instance4 = Badmash02.getInstance();
System.out.printf("饿汉-静态代码块初始化: %s\n", instance3 == instance4);
//懒汉-线程不安全
Idler01 instance5 = Idler01.getInstance();
Idler01 instance6 = Idler01.getInstance();
System.out.printf("懒汉-线程不安全: %s\n", instance5 == instance6);
//懒汉-线程不安全
Idler02 instance7 = Idler02.getInstance();
Idler02 instance8 = Idler02.getInstance();
System.out.printf("懒汉-线程安全: %s\n", instance7 == instance8);
//内部类
OutClass instance9 = OutClass.getInstance();
OutClass instance10 = OutClass.getInstance();
System.out.printf("内部类: %s\n", instance9 == instance10);
//枚举
Enum instance11 = Enum.getInstance();
Enum instance12 = Enum.getInstance();
System.out.printf("枚举: %s\n", instance11 == instance12);
}
}