现在大多数Java软件工程师面试都会问到这个问题:什么是单例模式(Singleton),能否写出单例模式的示例代码?单例模式是Gof中23个模式中最简单最容易入门的模式,学习它我们能更理性的感知模式的意义.
[形成]
Singleton Pattern 为什么会出现?在我们软件开发和架构中,经常遇到这样的情形:我们需要一个类只能且仅能产生一个实例..比如表示一台计算机的类,表示系统设定的类或者是表示窗口的类,还有为了节约资源,只让产生一个实例..
如何构造这种情形?Singleton模式给我一个方案:
[代码示例]
程序列表
名称
|
说明
|
Singleton
|
只有一个对象实例的类
|
Main
|
测试用的类
|
[UML图]
[示例代码和类的诠释]
1 package singleton;
2
3 public class Singleton {
4 private static Singleton singleton = new Singleton();
5
6 private Singleton() {
7 System.out.println("Create instance...");
8 }
9
10 public static Singleton getInstance() {
11 return singleton;
12 }
13 }
14
Singleton Class:
1.该类只能产生一个对象实例
2.把该类的的singleton属性设定为static再以Singleton;类的对象实例进行初始化,这个初始化的过程仅加载Sington类的时候调用一次.(Line4)
3.把Singleton 类的构造函数限定为private,目的是为了防止从非Singleton类(其他类)调用构造函数来产生实例,如果通过new方式来产生Singleton实例,会出现编译错误.这样做是为了保险己见.(Line6)
4.要得到Singleton实例的唯一方法就是调用类静态方法getInstance().这个名字可以随便取,只要方便理解就行.(Line 10)
1 package singleton;
2
3 public class Main {
4
5 public static void main(String[] args) {
6 System.out.println("Start");
7 Singleton obj1 = Singleton.getInstance();
8 Singleton obj2 = Singleton.getInstance();
9 if(obj1 == obj2){
10 System.out.println("obj1和obj2是同一個對象實例");
11 }else{
12 System.out.println("obj1和obj2不是同一個對象實例");
13 }
14 System.out.println("End");
15 }
16 }
17
Main Class
1.该类是测试程序.
2.程序通过getInstance()方式产生两个obj1和obj2实例.(Line 7,Line 8)
3.通过ojb1= =ojb2表达式来确定两个对象是否相同,判定是否产生了Singleton的第二个示例.(Line9-12)
执行结果含义:
1. 的确如此,obj1和obj2是Singleton类的同一个且唯一的对象实例.
2.当程序执行后,第一次调用getInstance的时候会初始化Singleton类,同时也会初始化static字段,也同时产生产生了一个唯一对象实例.
[拓展思考]
如下的另一一个单例模式的程序有什么隐患?
1 package singleton;
2
3 public class Singleton2 {
4
5 private static Singleton2 singleton = null;
6
7 private Singleton2(){
8 System.out.println("已產生對象實例");
9 }
10 public static Singleton2 getInstance(){
11 if(singleton == null){
12 singleton = new Singleton2();
13 }
14 return singleton;
15 }
16
17 }
18
[解答]
当多线程同时调用Singleton2.getInstance()方法时,可能会产生多个对象实例,例如
public class Main extends Thread{
public static void main(String[] args) {
System.out.println("Starts.");
new Main("A").start();
new Main("B").start();
new Main("C").start();
System.out.println("End.");
}
public void run(){
Singleton2 obj = Singleton2.getInstance();
System.out.println(getName()+": obj ="+obj);
}
public Main(String name){
super(name);
}
}
public class Singleton2 {
private static Singleton2 singleton2 = null;
private Singleton2(){
System.out.println("已产生对象实例");
solwDown();
}
public static Singleton2 getInstance(){
if(singleton2 == null){
singleton2 = new Singleton2();
}
return singleton2;
}
private void solwDown(){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
执行结果:
Start.
End.
已产生对象实例.
已产生对象实例.
已产生对象实例.
B: obj = Singleton2#2a9348
C: obj = Singleton2#b91134
A: obj = Singleton2#e343l12
(#替换为@)
之所以会知道这种情况是因为if(singleton = = null){ singleton = new Singleton2(); }判断不够严谨的导致。
利用: singleton == null 判断为空后去执行new Singleton2()之前,可能会有其他线程来抢先判断表达式singleton == null,从而又执行一遍创建实例的操作。
解决办法:
给getInstance()方法添加Synchronized修饰符,即可修改成线程安全严谨的单例模式。
public class Singleton2 {
private static Singleton2 singleton = null;
private Singleton2() {
System.out.println("已产生对象实例");
solwDown();
}
public static synchronized Singleton2 getInstance() {
if (singleton == null) {
singleton = new Singleton2();
}
return singleton;
}
private void solwDown() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}