目录
一、什么是单例模式
确保在程序中只生成一个实例的模式我们称之为单例模式(Singleton)。
二、示例程序
1、Singleton类
package com.as.module.Singeton;
/**
* 单例类
* @author Andy
* @date 2021/4/24 21:04
*/
public class Singleton {
public static Singleton singleton = new Singleton();
private Singleton(){
System.out.println("生成了一个实例");
}
public static Singleton getInstance(){
return singleton;
}
}
2、测试类
package com.as.module.Singeton;
/**
* TODO
*
* @author Andy
* @date 2021/4/24 21:06
*/
public class TestSingleton {
public static void main(String[] args) {
System.out.println("Start");
Singleton obj1 = Singleton.getInstance();
Singleton obj2 = Singleton.getInstance();
if(obj1==obj2){
System.out.println("obj1和obj2是相同的");
}else{
System.out.println("obj1和obj2是不同的");
}
System.out.println("end.");
}
}
3、运行结果
三、UML图
单例模式中的登场角色:
1、Singleton
在Singleton模式中,只有Singleton这一个角色。Singleton角色中有一个返回唯一实例的static方法。该方法总是会返回同一个实例。
四、拓展思路与项目实战
1、为什么必须设置限制
Singleton模式对实例的数量设置了限制。为什么要在程序中特意设置这个限制呢?设置限制其实就是为程序增加一项前提条件。
当存在多个实例时,实例之间相互影响,可能会产生意想不到的Bug。
但是,如果我们可以确保只有一个实例,就可以在这个前提条件下放心地编程了
2、何时生成这个唯一的实例
在程序运行后,第一次调用getInstance方法时,Singleton类会被初始化。也就是在这个时候,static字段singleton被初始化,生成了唯一的一个实例。
3、如何确保只生成一个实例
我们在Singleton模式中定义了用于获取唯一一个实例的static方法,同时,为了防止不小心使用new关键字创建实例,还将构造函数设置为private.
4、如何生成最多三个Triple类的实例
Triple类
package com.as.module.Singeton;
/**
* 三实例
* @author Andy
* @date 2021/4/24 21:26
*/
public class Triple {
private int id;
public static Triple[] triple = new Triple[]{
new Triple(0),
new Triple(2),
new Triple(2)
};
private Triple(int id){
System.out.println("The instance "+id+" is created");
}
public static Triple getInstance(int id){
return triple[id];
}
@Override
public String toString() {
return "Triple{" +
"id=" + id +
'}';
}
}
测试类:
package com.as.module.Singeton;
/**
* TODO
*
* @author Andy
* @date 2021/4/24 21:06
*/
public class TestSingleton {
public static void main(String[] args) {
// System.out.println("Start");
// Singleton obj1 = Singleton.getInstance();
// Singleton obj2 = Singleton.getInstance();
// if(obj1==obj2){
// System.out.println("obj1和obj2是相同的");
// }else{
// System.out.println("obj1和obj2是不同的");
// }
// System.out.println("end.");
System.out.println("Start");
for(int i=0;i<9;i++){
Triple triple = Triple.getInstance(i%3);
System.out.println(i+":"+i);
}
System.out.println("end.");
}
}
测试运行结果:
在上述代码中,triple字段并不是实例的字段,而是静态字段。triple字段的初始化只会在第一次生成时进行,之后生成Triple类的实例时不会再初始化triple字段。如果不将triple字段定义为静态字段,就会进入无限循环,在运行时会报错。
5、关于线程安全的探讨
示例代码如下(也称为懒汉式单例):
package com.as.module.Singeton;
/**
* 懒汉式单例
* @author Andy
* @date 2021/4/24 21:54
*/
public class SluggardSingleton {
private static SluggardSingleton sluggardSingleton = null;
private SluggardSingleton(){
System.out.println("生成了一个实例");
//确保能生成多个实例,线程睡1秒中
slowdown();
}
public static SluggardSingleton getInstance(){
if(sluggardSingleton==null){
sluggardSingleton = new SluggardSingleton();
}
return sluggardSingleton;
}
private void slowdown(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
其中,为了确保能出现生成多实例现象,故意降低了程序处理速度
测试类:
package com.as.module.Singeton;
/**
* TODO
*
* @author Andy
* @date 2021/4/24 21:06
*/
public class TestSluggardSingleton extends Thread{
public static void main(String[] args) {
System.out.println("Start.");
new TestSluggardSingleton("A").start();
new TestSluggardSingleton("B").start();
new TestSluggardSingleton("C").start();
}
public void run(){
SluggardSingleton obj = SluggardSingleton.getInstance();
System.out.println(getName()+":obj="+obj);
}
public TestSluggardSingleton(String name){
super(name);
}
}
运行结果:
由执行结果可见,如下条件判断不是线程安全的:
在使用sluggardSingleton==null判断第一个实例是否为null后,执行了下面的语句。
sluggardSingleton = new SluggardSingleton();
但是,在赋值之前,其他线程可能会进行sluggardSingleton==null判断。
因此,只有将getInstance()方法定义为synchronized方法后才是严谨的Singleton模式
再次执行测试,基于懒汉式的单例模式全局只有一个实例