单例模式
单例模式(Singleton)的目的是为了保证在一个进程中,某个类有且仅有一个实例。
单例模式的实现
1、饿汉式(静态常量)
实现步骤 1)构造方法私有化; 2)类的内部使用静态字段来引用唯一创建的对象实例; 3)向外提供一个静态的方法。 代码实现
public class Singleton {
// 2.本类内部一个对象实例
private static final Singleton INSTANCE = new Singleton();
// 1.构造方法私有化
private Singleton() {}
// 3.提供一个共有的静态方法,返回实例对象
public static Singleton getInstance() {
return INSTANCE;
}
}
这种实现方式很简单: 1. 只有private构造方法,确保外部无法实例化; 2. 通过private static变量持有唯一实例,保证全局唯一性; 3. 通过public static方法返回此唯一实例,使外部调用方能获取到实例。 优缺点: 1)优点 写法简单,在类加载的时候就完成实例化。避免了线程同步问题。 2)缺点 在类加载的时候完成实例化,没有达到 Lazy Loading(懒加载)的效果。如果从始至终都没有使用过这个实例,就会造成内存的浪费。
2、饿汉式(静态代码块)
实现步骤 1)构造方法私有化; 2)类的内部使用静态字段来引用唯一创建的对象实例; 3)使用静态代码块初始化实例对象 4)向外提供一个静态的方法。 代码实现
public class Singleton {
// 2.本类内部创建一个静态变量来引用实例对象
private static Singleton instance;
// 3.静态代码块初始化实例对象
static {
instance = new Singleton();
}
// 1.构造方法私有化
private Singleton() {}
// 4.提供一个静态方法来返回实例对象
public static Singleton getInstance() {
return instance;
}
}
优缺点与上一种实现方式一样。
3、懒汉式(线程不安全)
实现步骤 1)构造方法私有化; 2)类的内部使用静态字段来引用唯一创建的对象实例; 3)向外提供一个静态的方法,只有使用到该方法时才去创建实例。 代码实现
public class Singleton {
// 2.本类内部创建一个静态变量来引用实例对象
private static Singleton instance;
// 1.构造方法私有化
private Singleton() {
}
// 3.提供一个静态方法来返回实例对象
public static Singleton getInstance() {
// 只有使用到该方法时才去创建实例
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优缺点: 1)起到了延迟加载的效果,但是只能在单线程下使用。 2)如果在多线程下,一个线程进入if (instance == null) 判断,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。
4、懒汉式(线程安全,同步方法)
实现步骤 1)构造方法私有化; 2)类的内部使用静态字段来引用唯一创建的对象实例; 3)向外提供一个静态的同步(synchronized)方法,只有使用到该方法时才去创建实例。 代码实现
public class Singleton {
// 2.本类内部创建一个静态变量来引用实例对象
private static Singleton instance;
// 1.构造方法私有化
private Singleton() {
}
// 3.提供一个静态方法来返回实例对象
public static synchronized Singleton getInstance() {
// 只有使用到该方法时才去创建实例
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优缺点: 1)解决多线程下的线程安全问题 2)但加锁会严重影响并发性能,导致效率低。
5、懒汉式(线程安全,同步代码块)
实现步骤 1)构造方法私有化; 2)类的内部使用静态字段来引用唯一创建的对象实例; 3)向外提供一个静态的(使用同步代码块)方法,只有使用到该方法时才去创建实例。 代码实现
public class Singleton {
// 2.本类内部创建一个静态变量来引用实例对象
private static Singleton instance;
// 1.构造方法私有化
private Singleton() {
}
// 3.提供一个静态方法来返回实例对象
public static Singleton getInstance() {
// 只有使用到该方法时才去创建实例
if (instance == null) {
synchronized(Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}
优缺点: 1)这种方法,本意是想对第四种方式的改进,因为前面同步方法效率太低,改进为同步产生实例化的代码块 问题: 并不能起到线程同步的作用。在实际开发中,不能使用该方式。
6、双重检查
代码实现
public class Singleton {
// 2.使用volatile关键字
private static volatile Singleton instance;
// 1.构造方法私有化
private Singleton() {
}
// 提供一个静态方法来返回实例
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
优缺点: 1)双᯿锁的⽅式是⽅法级锁的优化,减少了部分获取实例的耗时。 2)同时这种⽅式也满⾜了懒加载。 推荐使用
7、静态内部类
实现步骤 1)构造方法私有化 2)使用静态内部类来创建实例 3)提供一个静态方法来返回实例 代码实现
public class Singleton {
// 1.构造方法私有化
private Singleton() {}
// 2.使用静态内部类来创建实例
private static class SingletonInstance{
private static Singleton instance = new Singleton();
}
// 3.提供一个静态方法来返回实例
public static Singleton getInstance() {
return SingletonInstance.instance;
}
}
优缺点: 使⽤类的静态内部类实现的单例模式,既保证了线程安全有保证了懒加载,同时不会因为加锁的⽅ 式耗费性能。 这主要是因为JVM虚拟机可以保证多线程并发访问的正确性,也就是⼀个类的构造⽅法在多线程环境下可以被正确的加载。 推荐使⽤
8、枚举
Java保证枚举类的每个枚举都是单例 代码实现
public enum Singleton {
INSTANCE;
public void test() {
System.out.println("使用枚举类创建单例");
}
}
优缺点: 1)线程安全、⾃由串⾏化、单⼀实例。 2)在存在继承场景下是不可⽤的。 推荐使用