单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。
饿汉式单例模式
优点
- 线程安全
- 在类加载的同事已经创建好一个静态对象,调用时反应速度快
缺点
- 资源效率不高,可能 getInstance() 永远不会执行到
- 执行该类的其他静态方法或者加载了该类 (class.forName),那么这个实例仍然能初始化
public class HungrySingleton {
private final static HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return instance;
}
public static void main(String[] args) {
/** 线程安全 */
int count = 1000;
CountDownLatch countDownLatch = new CountDownLatch(count);
for (int i = 0; i < count; i++) {
new Thread(() -> {
HungrySingleton instance = HungrySingleton.getInstance();
System.out.println(System.currentTimeMillis() + ":" + instance);
countDownLatch.countDown();
}).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
/** 通过反射 那么这个实例仍然能初始化 */
// try {
// Class<?> clazz = Class.forName("hungry.HungrySingleton");
// Object instance1 = clazz.newInstance();
// Object instance2 = clazz.newInstance();
//
// System.out.println(instance1);
// System.out.println(instance2);
// } catch (ClassNotFoundException e) {
// e.printStackTrace();
// } catch (IllegalAccessException e) {
// e.printStackTrace();
// } catch (InstantiationException e) {
// e.printStackTrace();
// }
}
}
懒汉式单例模式
Lazy_v1_SimpleSingleton
优点
- 避免了饿汉式的那种在没有用到的情况下创建实例,资源利用率高
缺点
- 线程不安全
- 执行该类的其他静态方法或者加载了该类 (class.forName),那么这个实例仍然初始化
public class Lazy_v1_SimpleSingleton {
private static Lazy_v1_SimpleSingleton instance;
private Lazy_v1_SimpleSingleton() {
}
public static Lazy_v1_SimpleSingleton getInstance() {
if (instance == null) {
instance = new Lazy_v1_SimpleSingleton();
}
return instance;
}
}
Lazy_v2_SynchronizedSingleton
优点
- 避免了饿汉式的那种在没有用到的情况下创建实例,资源利用率高
- 线程安全
缺点
- 第一次加载时不够快,多线程使用不必要的同步开销大
- 执行该类的其他静态方法或者加载了该类(class.forName),那么这个实例仍然初始化
public class Lazy_v2_SynchronizedSingleton {
private static Lazy_v2_SynchronizedSingleton instance;
private Lazy_v2_SynchronizedSingleton() {
}
public synchronized static Lazy_v2_SynchronizedSingleton getInstance() {
if (instance == null) {
instance = new Lazy_v2_SynchronizedSingleton();
}
return instance;
}
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("lazy.Lazy_v2_SynchronizedSingleton");
Object instance1 = clazz.newInstance();
Object instance2 = clazz.newInstance();
System.out.println(instance1);
System.out.println(instance2);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
Lazy_v3_DoubleCheckSingleton
优点
- 避免了饿汉式的那种在没有用到的情况下创建实例,资源利用率高
- 线程安全
缺点
- 执行该类的其他静态方法或者加载了该类(class.forName),那么这个实例仍然初始化
- 由于java内存模型一些原因偶尔失败
- 非原子操作
- 给 instance 分配内存
- 调用 instance 的构造函数来初始化成员变量,形成实例
- 将 instance 对象指向分配的内存空间(执行完这步 singleton才是非 null 了) --> 123 or 132
- 结论:如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错;由于有一个『instance已经不为null但是仍没有完成初始化』的中间状态,而这个时候,如果有其他线程刚好运行到第一层 if (instance == null) 这里,这里读取到的 instance 已经不为 null 了,所以就直接把这个中间状态的instance拿去用了,就会产生问题。
- 解决办法
- Lazy_v4_VolatileDoubleCheckSingleton 不让指令重排序 -lazy.Lazy_v5_InnerClassSingleton 可以指令重排序,但是不让外部看见
public class Lazy_v3_DoubleCheckSingleton {
private static Lazy_v3_DoubleCheckSingleton instance;
private Lazy_v3_DoubleCheckSingleton() {
}
public static Lazy_v3_DoubleCheckSingleton getInstance() {
if (instance == null) {
synchronized (Lazy_v3_DoubleCheckSingleton.class) {
if (instance == null) {
instance = new Lazy_v3_DoubleCheckSingleton();
}
}
}
return instance;
}
public static void main(String[] args) {
long count = 1000000;
synchronizedSingleton(count);
noSynchronizedSingleton(count);
}
private static void synchronizedSingleton(long count) {
long start = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
Lazy_v3_DoubleCheckSingleton synchronizedSingleton = Lazy_v3_DoubleCheckSingleton.getInstance();
}
long end = System.currentTimeMillis();
System.out.println("synchronizedSingleton 耗时:" + (end - start));
}
private static void noSynchronizedSingleton(long count) {
long start = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
HungrySingleton hungrySingleton = HungrySingleton.getInstance();
}
long end = System.currentTimeMillis();
System.out.println("noSynchronizedSingleton 耗时:" + (end - start));
}
}
Lazy_v4_VolatileDoubleCheckSingleton
优点
- 避免了饿汉式的那种在没有用到的情况下创建实例,资源利用率高
- 线程安全
缺点
- 执行该类的其他静态方法或者加载了该类(class.forName),那么这个实例仍然初始化
- 禁止指令重排,导致性能下降
public class Lazy_v4_VolatileDoubleCheckSingleton {
/** volatile关键字的一个作用是禁止指令重排,把instance声明为volatile之后,对它的写操作就会有一个内存屏障 */
private static volatile Lazy_v4_VolatileDoubleCheckSingleton instance;
private Lazy_v4_VolatileDoubleCheckSingleton() {
}
public static Lazy_v4_VolatileDoubleCheckSingleton getInstance() {
if (instance == null) {
synchronized (Lazy_v4_VolatileDoubleCheckSingleton.class) {
if (instance == null) {
instance = new Lazy_v4_VolatileDoubleCheckSingleton();
}
}
}
return instance;
}
}
Lazy_v5_InnerClassSingleton
优点
- 避免了饿汉式的那种在没有用到的情况下创建实例,资源利用率高
- 线程安全
缺点
- 执行该类的其他静态方法或者加载了该类(class.forName),那么这个实例仍然初始化
- 禁止指令重排,导致性能下降
public class Lazy_v5_InnerClassSingleton {
/**
* static 是为了使单例的空间共享
* final 保证这个方法不会被重写,重载
*/
public static final Lazy_v5_InnerClassSingleton getInstance() {
return SingletonHolder.instance;
}
/**
* 这种写法非常巧妙:
* 对于内部类 SingletonHolder,它是一个饿汉式的单例实现,在SingletonHolder初始化的时候会
* 由ClassLoader来保证同步,使 instance 是一个真·单例。
* 同时,由于SingletonHolder是一个内部类,只在外部类的Singleton的getInstance()中被使用,
* 所以它被加载的时机也就是在getInstance()方法第一次被调用的时候。
* <p>
* 从内部看是一个饿汉式的单例,但是从外部看来,又的确是懒汉式的实现。
*/
private static class SingletonHolder {
private static final Lazy_v5_InnerClassSingleton instance = new Lazy_v5_InnerClassSingleton();
}
/** 防止 反射 攻击! */
private static boolean initialized = false;
private Lazy_v5_InnerClassSingleton() {
synchronized (Lazy_v5_InnerClassSingleton.class){
if(initialized == false){
initialized = !initialized;
}
else{
throw new RuntimeException("单例已被侵犯");
}
}
}
public static void main(String[] args) {
try {
Class<?> clazz = Lazy_v5_InnerClassSingleton.class;
Constructor c = clazz.getDeclaredConstructor(null);
c.setAccessible(true);
Object o = c.newInstance();
System.out.println(o);
Object o1 = c.newInstance();
System.out.println(o1);
Object o2 = c.newInstance();
System.out.println(o2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
防止序列反序列话单例模式
public class SerializableSingleton implements Serializable {
private static final long serialVersionUID = -1994001829338263901L;
private SerializableSingleton() {
}
public final static SerializableSingleton INSTANCE = new SerializableSingleton();
public static SerializableSingleton getInstance(){
return INSTANCE;
}
/**
* @method_name: readResolve
* @describe: 防止 序列化和反序列化后 破坏单例模式规则 启用 readResolve() 方法
**/
public Object readResolve() throws ObjectStreamException{
return INSTANCE;
}
public static void main(String[] args) {
SerializableSingleton s1;
SerializableSingleton s2 = SerializableSingleton.getInstance();
FileInputStream fis;
ObjectInputStream ois;
FileOutputStream fos = null;
try {
fos = new FileOutputStream("serialize.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.flush();
oos.close();
fis = new FileInputStream("serialize.obj");
ois = new ObjectInputStream(fis);
s1 = (SerializableSingleton) ois.readObject();
ois.close();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1 == s2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
注册式单例模式
RegisterSingleton
优点
- 登记式单例模式可以被挤成(饿汉和懒汉模式构造方法都是稀有的,因而不能被继承的)
- 登记式单例实际上维护的是一组单例类的实例,将这些实例存储到一个Map(登记簿)中,对于已经登记过的单例,则从工厂直接返回,对于没有登记的,则先登记,而后返回
public class RegisterSingletonChildA extends RegisterSingleton {
public static RegisterSingletonChildA getInstance() {
return (RegisterSingletonChildA) RegisterSingletonChildA
.getInstance("org.crayzer.demo.singleton.register.RegisterSingletonChildA");
}
//随便写一个测试的方法
public String about() {
return "---->我是RegisterSingleton的一个子类RegisterSingletonChildA";
}
}
public class RegisterSingletonChildB extends RegisterSingleton {
public static RegisterSingletonChildB getInstance() {
return (RegisterSingletonChildB) RegisterSingletonChildB
.getInstance("org.crayzer.demo.singleton.register.RegisterSingletonChildB");
}
//随便写一个测试的方法
public String about() {
return "---->我是RegisterSingleton的一个子类RegisterSingletonChildB";
}
}
public class RegisterSingleton {
private static Map<String, RegisterSingleton> registerSingletonMap = new ConcurrentHashMap<>();
public static Map<String, RegisterSingleton> getRegisterSingletonMap() {
return registerSingletonMap;
}
protected RegisterSingleton() {
System.out.println("RegisterSingleton 的构造函数被调用,创建实例中...");
}
public static synchronized RegisterSingleton getInstance(String name) {
if (name == null) {
name = RegisterSingleton.class.getName();
System.out.println("name不存在,name = " + name);
}
if (registerSingletonMap.get(name) == null) {
try {
System.out.println("-->name对应的值不存在,开始创建");
registerSingletonMap.put(name, (RegisterSingleton) Class.forName(name).newInstance());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}else {
System.out.println("-->name对应的值存在");
}
System.out.println("-->返回name对应的值");
return registerSingletonMap.get(name);
}
/**
* 测试 多线程环境
* @param args
*/
public static void main(String[] args) {
// testMultithreaded();
// System.out.println("=============华丽的分割线=============");
testExtends();
}
private static void testExtends() {
System.out.println("-----------------登记式单例模式----------------");
System.out.println("第一次取得实例(登记式)");
RegisterSingleton s1 = RegisterSingleton.getInstance(null);
System.out.println(s1);
System.out.println("第二次取得实例(登记式)");
RegisterSingletonChildA s3 = RegisterSingletonChildA.getInstance();
System.out.println(s3);
System.out.println(s3.about());
System.out.println("第三次取得实例(登记式)");
RegisterSingletonChildB s4 = RegisterSingletonChildB.getInstance();
System.out.println(s4);
System.out.println(s4.about());
System.out.println("第四次取得实例(登记式)");
RegisterSingletonChildB s5 = RegisterSingletonChildB.getInstance();
System.out.println(s5);
System.out.println(s5.about());
System.out.println("第五次取得实例(非正常直接new子类的构造方法)");
RegisterSingletonChildB s6 = new RegisterSingletonChildB();
System.out.println(s6);
System.out.println(s6.about());
System.out.println("输出父类中Map保存的所有单例,可以看出,直接new出来的实例并没有存在Map中");
System.out.println(s1.getRegisterSingletonMap());
System.out.println();
}
public static void testMultithreaded() {
int count = 100;
CountDownLatch latch = new CountDownLatch(count);
for (int i = 0; i < count; i++) {
new Thread(() -> {
RegisterSingleton singleton = RegisterSingleton.getInstance("org.crayzer.demo.singleton.register.RegisterSingleton");
System.out.println(System.currentTimeMillis() + ":" + singleton);
latch.countDown();
}).start();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
RegisterSingleton singleton = new RegisterSingleton();
System.out.println(singleton.getRegisterSingletonMap());
RegisterSingletonChildA singleton1 = new RegisterSingletonChildA();
System.out.println(singleton1.getRegisterSingletonMap());
RegisterSingletonChildB singleton2 = new RegisterSingletonChildB();
System.out.println(singleton2.getRegisterSingletonMap());
}
}
Enum
public enum EnumSingleton {
RED(){
private int r = 255;
private int g = 0;
private int b = 0;
},BLACK(){
private int r = 0;
private int g = 0;
private int b = 0;
},WHITE(){
private int r = 255;
private int g = 255;
private int b = 255;
};
public void func1() {
}
public static void main(String[] args) {
System.out.println(EnumSingleton.RED);
}
}
