单例模式的作用就是保证类只有一个实例对象。
分为懒汉式和饿汉式,听名字就能知道个大概了,直接贴代码。
懒汉式:
/**
* 懒汉代理模式
*/
public class Singleton {
//私有构造方法
private Singleton() {
}
// 单例对象
private static Singleton singleton = null;
public Singleton getSingleton() {
if (null == singleton) {
singleton = new Singleton();
}
return singleton;
}
}
线程安全问题: 如果有两个线程,同一时刻拿到单例对象,要去静态工厂办法访问,由于工厂办法没有锁,那么很有可能这两个线程最终会拿到两个实例。
饿汉式:
/**
* 饿汉式代理模式
*/
public class Singleton2 {
// 私有构造函数
private Singleton2() {
}
// 单例对象
private static Singleton2 singleton2 = new Singleton2();
// 静态的工厂方法
public static Singleton2 getInstance() {
return singleton2;
}
}
饿汉式和懒汉式相比,是线程安全的,缺点是比较耗费内存,应用一加载就会直接放到堆中。
线程安全的饿汉式:
public class Singleton3 {
// 私有构造函数
private Singleton3() {
}
// 单例对象 volatile + 双重检测机制 -> 禁止指令重排
private volatile static Singleton3 singleton3 = null;
// 静态的工厂方法
public static Singleton3 getInstance() {
if (singleton3 == null) { // 第一层检查,检查是否有引用指向对象,高并发情况下会有多个线程同时进入
synchronized (Singleton3.class) { // 第一层锁,保证只有一个线程进入
//双重检查,防止多个线程同时进入第一层检查(因单例模式只允许存在一个对象,故在创建对象之前无引用指向对象,所有线程均可进入第一层检查)
//当某一线程获得锁创建一个Singleton对象时,即已有引用指向对象,singleton不为空,从而保证只会创建一个对象
//假设没有第二层检查,那么第一个线程创建完对象释放锁后,后面进入对象也会创建对象,会产生多个对象
if (singleton3 == null) {
//volatile关键字作用为禁止指令重排,保证返回Singleton对象一定在创建对象后
singleton3 = new Singleton3(); // A - 3
}
}
}
return singleton3;
}
}