友情支持
如果您觉得这个笔记对您有所帮助,看在D瓜哥码这么多字的辛苦上,请友情支持一下,D瓜哥感激不尽,😜
有些打赏的朋友希望可以加个好友,欢迎关注D 瓜哥的微信公众号,这样就可以通过公众号的回复直接给我发信息。
公众号的微信号是: jikerizhi 。因为众所周知的原因,有时图片加载不出来。 如果图片加载不出来可以直接通过搜索微信号来查找我的公众号。 |
3. 单例模式
如果你不对构造方法做改动的话,是不可能阻止他人不去用new的。所以我们完全可以直接就把这个类的构造方法改成私有(private),你应该知道,所有类都有构造方法,不编码则系统默认生成空的构造方法,若有显示定义的构造方法,默认的构造方法就会失效。
客户端不再考虑是否需要去实例化的问题,而把责任都给了应该负责的类去处理。其实这就是一个很基本的设计模式:单例模式。
通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。[DP]
Singleton类,定义一个GetInstance操作,允许客户访问它的唯一实例。GetInstance是一个静态方法,主要负责创建自己的唯一实例。
单例模式除了可以保证唯一的实例
单例模式因为Singleton类封装它的唯一实例,这样它可以严格地控制客户怎样访问它以及何时访问它。简单地说就是对唯一实例的受控访问。
实用类通常也会采用私有化的构造方法来避免其有实例。
实用类不保存状态,仅提供一些静态方法或静态属性让你使用,而单例类是有状态的。实用类不能用于继承多态,而单例虽然实例唯一,却是可以有子类来继承。实用类只不过是一些方法属性的集合,而单例却是有着唯一的对象实例。
lock是确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。[MSDN]
这段代码使得对象实例由最先进入的那个线程创建,以后的线程在进入时不会再去创建对象实例了。由于有了lock,就保证了多线程环境下的同时访问也不会造成多个实例的生成。
不用让线程每次都加锁,而只是在实例未被创建的时候再加锁处理。同时也能保证多线程的安全。这种做法被称为Double-Check Locking(双重锁定)。
C#与公共语言运行库也提供了一种‘静态初始化’方法,这种方法不需要开发人员显式地编写线程安全代码,即可解决多线程环境下它是不安全的问题。[MSDN]
这样的实现与前面的示例类似,也是解决了单例模式试图解决的两个基本问题:全局访问和实例化控制,公共静态属性为访问实例提供了一个全局访问点。不同之处在于它依赖公共语言运行库来初始化变量。由于构造方法是私有的,因此不能在类本身以外实例化Singleton类;因此,变量引用的是可以在系统中存在的唯一的实例。不过要注意,instance变量标记为readonly,这意味着只能在静态初始化期间或在类构造函数中分配变量[MSDN]。由于这种静态初始化的方式是在自己被加载时就将自己实例化,所以被形象地称之为饿汉式单例类,原先的单例模式处理方式是要在第一次被引用时,才会将自己实例化,所以就被称为懒汉式单例类。[J&DP]
饿汉式,即静态初始化的方式,它是类一加载就实例化的对象,所以要提前占用系统资源。然而懒汉式,又会面临着多线程访问的安全性问题,需要做双重锁定这样的处理才可以保证安全。所以到底使用哪一种方式,取决于实际的需求。
3.1. 使用反射和反序列化产生多个“单例类”实例
D瓜哥根据以前学习设计模式的经验来看,单例模式的实现类只能产生一个对象。估计这也是我们大家普遍看法。但是,在一次面试时,被面试官问到“单例模式的实现类能否产生多个对象?”然后,D瓜哥当场就Hold不住了。
不过,当时善意的面试官提醒,可以用反射。昨天看《Java 程序性能优化》时,提到用反序列化也可以产生多个方法。然后,又被我同学问到这个问题。干脆写一篇文章,总结一下吧。
3.2. 定义
根据 GoF 的著名著作 《设计模式》,单例模式的定义如下:
- 单例模式(Singleton)
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
《设计模式》
在Java中,单例模式实现方式有两种:
-
静态初始化的方式是在自己被加载时就将自己实例化。这种方式被称为“饿汉式单例类”;
-
在第一次被引用时,才会将自己实例化。这种方式被称为“懒汉式单例类”。
下面做针对介绍。
3.3. 饿汉式单例类
饿汉式单例类的实现代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.diguage.didp.singleton;
/**
* 饿汉式单例类
*
* @author D瓜哥, https://www.diguage.com/
* @since 2014-5-26.
*/
public class HungrySingleton {
private static HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {}
public static HungrySingleton getInstance() {
return instance;
}
}
3.4. 懒汉式单例类
懒汉式单例类的实现代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.diguage.didp.singleton;
/**
* 懒汉式单例类
*
* <p>Coder:D瓜哥, https://www.diguage.com/
*
* <p>Date:2014-5-26.
*/
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (null == instance) {
synchronized (LazySingleton.class) {
if (null == instance) {
instance = new LazySingleton();
}
}
}
return instance;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.diguage.didp.singleton;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 单例模式并发性测试
*
* @author D瓜哥, https://www.diguage.com/
* @since 16/11/2016.
*/
public class ConcurrentTest {
public static void main(String[] args) {
int THREAD_COUNT = 10000;
CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
ConcurrentMap concurrentMap = new ConcurrentHashMap();
ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);
for (int i = 0; i < THREAD_COUNT; i++) {
executorService.execute(new SingletonFactory(latch, concurrentMap, i));
latch.countDown();
}
executorService.shutdown();
}
}
class SingletonFactory implements Runnable {
private CountDownLatch latch;
private ConcurrentMap concurrentMap;
private int id;
public SingletonFactory(CountDownLatch latch, ConcurrentMap concurrentMap, int id) {
this.latch = latch;
this.concurrentMap = concurrentMap;
this.id = id;
}
public void run() {
try {
latch.await();
LazySingleton instance = LazySingleton.getInstance();
concurrentMap.put(instance, instance);
System.out.println(id + "\t" + concurrentMap.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
volatile
修饰的懒汉式单例类 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.diguage.didp.singleton;
import java.io.Serializable;
/**
* 带 <code>volatile</code> 修饰的懒汉式单例类
*
* @author D瓜哥, https://www.diguage.com/
* @since 2014-5-26.
*/
public class VolatileLazySingleton implements Serializable {
private static volatile VolatileLazySingleton instance = null;
private VolatileLazySingleton() {}
public static VolatileLazySingleton getInstance() {
if (null == instance) {
synchronized (VolatileLazySingleton.class) {
if (null == instance) {
instance = new VolatileLazySingleton();
}
}
}
return instance;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.diguage.didp.singleton;
import java.io.Serializable;
/**
* 可以序列化的懒汉式单例类
*
* <p>注:不正确
*
* @author D瓜哥, https://www.diguage.com/
* @since 2014-5-26.
*/
public class SerializableLazySingleton implements Serializable {
private static volatile SerializableLazySingleton instance = null;
private SerializableLazySingleton() {}
public static SerializableLazySingleton getInstance() {
if (null == instance) {
synchronized (SerializableLazySingleton.class) {
if (null == instance) {
instance = new SerializableLazySingleton();
}
}
}
return instance;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.diguage.didp.singleton;
import java.io.Serializable;
/**
* 可以序列化的懒汉式单例类
*
* @author D瓜哥, https://www.diguage.com/
* @since 2014-5-26.
*/
public class CorrectSerializableLazySingleton implements Serializable {
private static volatile CorrectSerializableLazySingleton instance = null;
private CorrectSerializableLazySingleton() {}
public static CorrectSerializableLazySingleton getInstance() {
if (null == instance) {
synchronized (CorrectSerializableLazySingleton.class) {
if (null == instance) {
instance = new CorrectSerializableLazySingleton();
}
}
}
return instance;
}
private Object readResolve() {
return instance;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.diguage.didp.singleton;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* 序列化测试
*
* @author D瓜哥, https://www.diguage.com/
* @since 16/11/2016.
*/
public class SerializationTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
testSerialization(SerializableLazySingleton.getInstance());
testSerialization(CorrectSerializableLazySingleton.getInstance());
testSerialization(Singleton.INSTANCE);
}
public static <T> void testSerialization(T t) throws IOException, ClassNotFoundException {
// 将对象写入数组中
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(t);
baos.close();
byte[] objectByteArray = baos.toByteArray();
// 从数组中读取对象
ByteArrayInputStream bais = new ByteArrayInputStream(objectByteArray);
ObjectInputStream ois = new ObjectInputStream(bais);
T newInstance = (T) ois.readObject();
//判断是否是同一个对象
Class<?> clazz = t.getClass();
String canonicalName = clazz.getCanonicalName();
System.out.println(canonicalName + ": " + (newInstance == t));
}
}
1
2
3
4
5
6
7
8
9
package com.diguage.didp.singleton;
/**
* @author D瓜哥, https://www.diguage.com/
* @since 16/11/2016.
*/
public enum Singleton {
INSTANCE
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.diguage.didp.singleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* 反射测试
*
* @author D瓜哥, https://www.diguage.com/
* @since 16/11/2016.
*/
public class ReflectionTest {
public static void main(String[] args)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
InstantiationException {
testReflection(CorrectSerializableLazySingleton.getInstance());
// testReflection(Singleton.INSTANCE);
}
private static <T> void testReflection(T t)
throws NoSuchMethodException, InstantiationException, IllegalAccessException,
InvocationTargetException {
Class<?> clazz = t.getClass();
Class<?>[] params = {};
Constructor<?> constructor = clazz.getDeclaredConstructor(params);
constructor.setAccessible(true);
T instance = (T) constructor.newInstance();
System.out.println((instance == t) + "\t:\t" + clazz.getCanonicalName());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.diguage.didp.singleton;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
/**
* {@link Unsafe} 创建实例测试
*
* @author D瓜哥, https://www.diguage.com/
* @since 16/11/2016.
*/
public class UnsafeTest {
public static void main(String[] args)
throws NoSuchFieldException, IllegalAccessException, InstantiationException {
testUnsafe(CorrectSerializableLazySingleton.getInstance());
testUnsafe(Singleton.INSTANCE);
}
private static <T> void testUnsafe(T t)
throws NoSuchFieldException, IllegalAccessException, InstantiationException {
Unsafe unsafe = getUnsafe();
Class<?> clazz = t.getClass();
T instance = (T) unsafe.allocateInstance(clazz);
System.out.println((instance == t) + "\t:\t" + clazz.getCanonicalName());
}
private static Unsafe getUnsafe() throws NoSuchFieldException, IllegalAccessException {
// 通过反射得到theUnsafe对应的Field对象
Field field = Unsafe.class.getDeclaredField("theUnsafe");
// 设置该Field为可访问
field.setAccessible(true);
// 通过Field得到该Field对应的具体对象,传入null是因为该Field为static的
return (Unsafe) field.get(null);
}
}
两种方式各有优缺。饿汉式,即静态初始化的方式,它是类一加载就实例化对象。所以,加载时间比较长,而且要提前占用系统资源,如果后续用不到这个对象,会造成浪费。懒汉式,又会面临多线程访问的安全性问题,需要使用双重锁定才能保证安全性,由于 Java 虚拟机的问题,在 JDK5 以后双重锁定能可以正常工作;另外,由于是在使用时初始化,在第一次调用时耗时比较长。所以,至于到底使用哪种方式,取决于实际需求。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.diguage.didp.singleton;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 单例模式并发性测试
*
* @author D瓜哥, https://www.diguage.com/
* @since 16/11/2016.
*/
public class ConcurrentTest {
public static void main(String[] args) {
int THREAD_COUNT = 10000;
CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
ConcurrentMap concurrentMap = new ConcurrentHashMap();
ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);
for (int i = 0; i < THREAD_COUNT; i++) {
executorService.execute(new SingletonFactory(latch, concurrentMap, i));
latch.countDown();
}
executorService.shutdown();
}
}
class SingletonFactory implements Runnable {
private CountDownLatch latch;
private ConcurrentMap concurrentMap;
private int id;
public SingletonFactory(CountDownLatch latch, ConcurrentMap concurrentMap, int id) {
this.latch = latch;
this.concurrentMap = concurrentMap;
this.id = id;
}
public void run() {
try {
latch.await();
LazySingleton instance = LazySingleton.getInstance();
concurrentMap.put(instance, instance);
System.out.println(id + "\t" + concurrentMap.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3.5. 产生多个“单例类”实例
还回到文章开头,D瓜哥被问到的问题:单例模式的实现类能否产生多个对象?既然写这篇文章,那么答案肯定是肯定的。那么怎么产生呢?
有两种方式可以做到。我们这里分别来试验一下。
问题:既然 static
变量是类共享的,修改后,其他对象也应该立即修改,那么还需要 volidate
吗?
3.6. 枚举
An enum type has no instances other than those defined by its enum constants. It is a compile-time error to attempt to explicitly instantiate an enum type (§15.9.1).
In addition to the compile-time error, three further mechanisms ensure that no instances of an enum type exist beyond those defined by its enum constants:
The final clone method in Enum ensures that enum constants can never be cloned.
Reflective instantiation of enum types is prohibited.
Special treatment by the serialization mechanism ensures that duplicate instances are never created as a result of deserialization.
《The Java® Language Specification Java SE 17 Edition》 -- 8.9. Enum Types