单例实现步骤
1.构造方法私有化。
2.在该类内提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。
public class SingleObject {
private static SingleObject instance = new SingleObject();
private SingleObject(){}
public static SingleObject getInstance(){
return instance;
}
public void showMessage(){
System.out.println("hello world!");
}
}
public static void main(String[] args) {
//获取唯一可用的对象
SingleObject singleObject = SingleObject.getInstance();
//调用方法
singleObject.showMessage();
}
单例模式实现方式
1.懒汉式:线程不安全
单例模式最基本的实现方式,不支持多线程。多线程不安全。只能在单线程下使用,在多线程下使用的话,当一个线程执行到if (instance == null )时,还未来得及往下执行,另一个线程也通过了这个判断语句,则会产生多个instance实例。
private static Singleton instance;
private Singleton(){};
public static Singleton getInstance(){
if (instance == null ){
instance = new Singleton();
}
return instance;
}
2.懒汉式:线程安全
单例实例被延迟加载,可以在多线程中很好的工作,但效率很低。
优点:延迟加载,第一次调用才初始化,避免了内存的浪费。
缺点:必须加锁 synchronized才能保证单例,但加锁会影响效率。
private static Singleton instance;
private Singleton(){};
public static synchronized Singleton getInstance(){
if (instance == null ){
instance = new Singleton();
}
return instance;
}
3.饿汉式
常用,但容易产生垃圾对象。
优点:因为没有加锁,所以执行效率会提高。在类装载时就完成实例化,避免了线程同步问题。
缺点:类加载时就初始化,浪费内存。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){};
public static Singleton getInstance(){
return instance;
}
}
4.双检锁/双重校验锁(DCL,double-checked locking)
采用双锁机制,安全且在多线程情况下能保持高性能。
优点:线程安全;延迟加载;效率较高。
public class Singleton {
//volatile是Java提供的一种轻量级的同步机制。
private volatile static Singleton instance;
private Singleton(){};
public static Singleton getInstance(){
//先判断是否存在,不存在再加锁处理
if (instance == null){
//在同一个时刻加了锁的那部分程序只有一个线程可以进入
synchronized (Singleton.class){
if (instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
如果没有volatile关键字,在第5行会出现问题。instance = new TestInstance();可以分解为以下3行伪代码。
a. memory = allocate() //分配内存
b. ctorInstanc(memory) //初始化对象
c. instance = memory //设置instance指向刚分配的地址
上面的代码在编译运行时,可能会出现重排序从a-b-c排序为a-c-b。在多线程的情况下会出现以下问题。当线程A在执行第11行代码时,B线程进来执行到第7行代码。假设此时A执行的过程中发生了指令重排序,即先执行了a和c,没有执行b。那么由于A线程执行了c导致instance指向了一段地址,所以B线程判断instance不为null,会直接跳到第15行并返回一个未初始化的对象。
5.登记式/静态内部类
这种实现方式能达到和双检锁相同的效果,这种方式只适用于静态域的情况。
利用classloder机制来保证初始化instance时只有一个线程。与饿汉式不同的是,饿汉式在类装载时,instance就会被实例化。登记式在类装载时,instance不一定被初始化,因为SingletonHolder没有被主动使用,只有调用getInstance方法时,才有装载SingletonHolder类,才会去实例化instance。
public class Singleton {
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
private Singleton(){};
public static final Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
6.枚举
实现单例模式的最佳方法,能避免多线程同步问题,自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。实际工作中,很少用。
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}