单例模式的那些事
写在前面
单例Singleton设计模式, 老生常谈的一个设计模式。但你真的用对了么? 用的姿势很重要!
1.概念解释
单例顾名思义就是只产生一个实例对象。那怎样保证单一呢?把目标类提供给外部创建实例对象的能力收回,即构造函数设为私有,然后内部提供一个生成实例静态方法。设计成单例模式, 有各种各样的实现方式。
2.单例模式的设计
2.1 饿汉式单例
饿汉式单例是指在方法调用前,实例就已经创建好了。
-实现代码:
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
-验证高并发下的执行情况
public class SingletonClient {
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
final Singleton instance = Singleton.getInstance();
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(instance.hashCode());
}
});
}
for (Thread thread : threads) {
thread.start();
}
}
}
- 结果
165827088
165827088
165827088
165827088
165827088
165827088
165827088
165827088
165827088
165827088
2.2 懒汉式单例
懒汉式单例是指在方法调用获取实例时才创建实例,因为相对饿汉式显得“不急迫”,所以被叫做“懒汉模式”。
1)单线程的懒汉式单例 -- 初学者最容易犯错的写法.
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
try {
// 刻意模仿多线程访问下创建实例不是单一的情况
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new Singleton();
}
return instance;
}
}
上面创建单例在单线程下是木问题的。但在多线程访问下就会有问题啦。
如何改进呢?来,再往下看
2)线程安全的懒汉式单例.
-同步方法锁定
同步方法效率很低, 它把真个方法都加了锁, 在高并发下, 只允许一个线程进入到该方法
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
-同步代码块锁定
同步代码块效率也很低,但比同步方法效率高点, 它把高并发的代码块加了锁, 在高并发下, 只允许一个线程进入到该代码块。
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
try {
synchronized (Singleton.class) {
if (instance == null) {
Thread.sleep(100);
instance = new Singleton();
}
}
} catch (Exception e) {
}
return instance;
}
}
-双检查锁机制(推荐)
这种方式不仅能保证线性安全, 还能提高效率。因为volatile关键字保证多线程间的可见性;在同步块中使用二次检查,以保证其不被重复实例化。集合其二者,这种实现方式既保证了其高效性,也保证了其线程安全性。
public class Singleton {
// volatile 关键词修饰
volatile private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
try {
if (instance == null) {
synchronized (Singleton.class) {
Thread.sleep(100);
instance = new Singleton();
}
}
} catch (Exception e) {
}
return instance;
}
}
3)使用静态内置类实现单例模式.
public class Singleton {
private Singleton() {
}
private static class SingletonHandler {
private static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHandler.instance;
}
}
4)使用静态代码块实现单例模式
public class Singleton {
private static Singleton instance;
static {
instance = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
写在后面