定义:确保一个类只有一个实例,并提供一个全局访问点。
注意:单件模式是最简单的设计模式,但想要正确的使用单件模式,仍然有许多需要注意的地方。
要点:
- 单件模式确保程序中一个类最多只有一个实例。
- 单件模式提供这个实例的全局访问点。
- 在java中实现单件模式需要私有的构造器、一个静态方法和一个静态变量。
老生常谈,有两种单件模式,懒汉式和饿汉式,具体如下:
懒汉式(延迟实例化):用到的时候才开始实例化对象,适用于实例化对象耗内存、耗性能等情况,以免应用程序没有用到此对象而白白浪费资源。
//懒汉式
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance(){
if(uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
饿汉式:适用于应用程序总是创建并使用单件模式,或者在创建和运行时的负担不繁重。JVM在加载类时马上创建此唯一的单件实例。
//饿汉式
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() {}
public static Singleton getInstance(){
return uniqueInstance;
}
}
懒汉式存在的问题:在多线程的环境下很可能出现问题,程序中可能创建多个实例。
if(uniqueInstance == null) {
uniqueInstance = new Singleton();
}
处理多线程:
遇到这种情况,我们很容易想到synchronized同步方法,可以轻易的解决遇到的问题。
public static synchronized Singleton getInstance(){
if(uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
存在两个问题:
- 同步会降低性能。
- 只有第一次执行此方法时,才真正需要同步,一旦设置了uniqueInstance变量,就不在需要同步这个方法了。之后每次调用这个方法,同步都是一种累赘。
如何改变多线程?
- 如果性能对应用程序不是很关键,就什么都不做。
- 使用饿汉式,直接创建实例,而不用延迟实例化的做法。
- 用“双重检查加锁”,在getInstance()中减少使用同步。(不适用于1.4及更早版本的java)
private volatile static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance(){
if(uniqueInstance == null) {
synchronized (Singleton.class) {
if(uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
volatile关键词确保:当uniqueInstance变量被初始化成Singleton实例时,多个线程正确地处理uniqueInstance变量。
利用双重检查加锁,首先检查是否实例已经创建了,如果尚未创建,才进行同步,只有第一次会同步。
总结:
全文介绍了单件模式的两种典型案例,并剖析了多线程情况下单件模式的优化方案,我们可以在项目中视情况而定,采用适合的方案。