单例模式的几个要点
- 声明一个自身类型的私有静态类实例
- 必须声明私有构造函数,防止进行实例化
- 定义公有的静态方法,访问私有静态的自身类型对象实例
单例模式常见的使用场景
- 使用一个实例对象读写配置文件
缺点:
- 违背单一职责,单例模式无法进行扩展,如何要扩展只能修改代码。
最简化单例模式,但这种无法防止多线程情境下生成多个实例对象问题
public class Singleton {
private static Singleton instance; // 定义一个静态的唯一私有实例对象
private Singleton() { // 私有化构造函数
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton(); // 实例化对象
}
return instance; // 返回实例化对象
}
}
前面说了上述方式无法防止多线程情境下也只有一个实例对象的问题。现在进行多线程情况下进行一个测试
public class SingletonTest {
private static int clientNum = 100;
// 需要使用CountdownLatch计数器
static CountDownLatch doneSingal = new CountDownLatch(clientNum);
public static void main(String[] args) {
// 使用线程池,线程池执行获取单例实例的方法
ExecutorService exec = Executors.newFixedThreadPool(10);
for (int i = 0; i < clientNum; i++) {
exec.execute(new MyRunnable());
doneSingal.countDown();
}
}
static class MyRunnable implements Runnable {
@Override
public void run() {
// 获取单例实例 Singleton.getInstance()
try {
doneSingal.await(); // 好像是计数器进行等待
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Singleton.getInstance());
}
}
}
- 执行结果:
获取实例对象hashCode:1859116143 十六进制的hashCode:6ecfdc6f
DesignPatternEx.Singleton.Singleton@6ecfdc6f
DesignPatternEx.Singleton.Singleton@6ecfdc6f
......
......
DesignPatternEx.Singleton.Singleton@6ecfdc6f
DesignPatternEx.Singleton.Singleton@6ecfdc6f
获取实例对象hashCode:1730944665 十六进制的hashCode:672c1e99
DesignPatternEx.Singleton.Singleton@6ecfdc6f
DesignPatternEx.Singleton.Singleton@6ecfdc6f
DesignPatternEx.Singleton.Singleton@672c1e99
DesignPatternEx.Singleton.Singleton@672c1e99
DesignPatternEx.Singleton.Singleton@672c1e99
......
......
DesignPatternEx.Singleton.Singleton@672c1e99
DesignPatternEx.Singleton.Singleton@672c1e99
通过执行结果可以看到在多线程情况下对象实例化了两次,这就导致单例模式不会永远只会拿到一个实例。