文章目录
1、什么是单例模式?
单例模式指无论调用实例方法(返回实例的方法)多少次,都保持同一个实例的设计模式。
2、如何实现单例模式?
/**
* 单例模式类
*/
class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton newInstance() {
return null;
}
}
这就是单例模式的实现思路。
2.1 实现思路
类外获取实例的方法不是通过new或者反射,而是通过调用newInstance()。三点说明:
- 1、instance为静态私有,一是为了静态newInstance()可获取到,二是防止类外直接获取;
- 2、构造方法设置为私有,即无法主动实例化(new).
- 3、newInstance()是静态公共方法,static是因为Singleton无法实例化,即无对象可以调用newInstance();public是为了方便类外调用。
3、单例模式有几种形式?
3.1 “饿汉式”:
/**
* 单例模式类
*/
class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton newInstance() {
return instance;
}
}
特点:唯一实例在类内直接创建,不存在多实例可能,不会违背“单例”。
3.2 “懒汉式”:
/**
* 单例模式类
*/
class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton newInstance() {
if (instance == null) {
instance = new Singleton();
}
return new Singleton();
}
}
特点:当newInstance()被调用时,才创建实例。
3.2.1 线程不安全问题
这个方法表面上看没有问题,可实际上,由于这个方法本身是线程不安全的,当多个线程同时调用时,就可能导致创建多个实例,违背“单例”。举个例子:
public static Singleton newInstance() {
if (instance == null) {---------------a
instance = new Singleton();-------b
}
return new Singleton();
}
有两个线程x、y同时调用newInstance(),x先执行a,判断instance 是否存在,为true,可此时,x的CPU时间片用完,CPU被y抢去。y也执行a,判断instance 是否存在,为true,执行b,创建一个实例。然后,x重新获得时间片,继续执行,由于x已经判断过instance ,为true就执行b,又创建一个实例。这样,就违背了“单例“。
在高并发下,这种情况很容易发生,而且这只是其中一种情况。
4、如何解决“懒汉式”线程安全问题?
4.1 “锁方法”:
public static synchronized Singleton newInstance() {
if (instance == null) {
instance = new Singleton();
}
return new Singleton();
}
}
将newInstance()用synchronized同步锁锁住,这样一次就只有一个线程能进入newInstance()。
4.2 “锁代码块”:
public static Singleton newInstance() {
synchronized (Singleton.class) {
if (instance == null) {-------------a
instance = new Singleton();-----b
}
}
return new Singleton();
}
}
因为可能会出现线程安全问题的代码就是a,故用synchronized 将其锁住,原理与“锁方法”相同。
4.3 “双重检测机制”:
public static Singleton newInstance() {
if (instance == null) {--------------------a
synchronized (Singleton.class) {-------b
if (instance == null) {------------c
instance = new Singleton();----d
}
}
}
return new Singleton();---------------------e
}
在上述举例的会导致线程安全的例子中,根本原因就是未对实例存在做双重检测。
4.3.1 代码读解:
以上述举例的会导致线程安全的例子为背景。
当x的时间片用完,CPU被y抢去时,y一路畅通,执行d创建实例。然后,x执行c重新判断instance是否存在,由于y已经创建实例,故instance存在,因此,x执行e返回instance,实例唯一。
必须用synchronized将c锁住,不然与“懒汉式”别无二致,无意义。
大家可以模拟各种并发时线程行为,自行推演一下。
文章完结。