1 饿汉模式
特点:类装载时就完成初始化,是比较简单而有效的单例模式
缺点:不一定会用到这个实例,浪费内存
public class Singleton01 {
private static final Singleton01 INSTANCE01 =new Singleton01();
private Singleton01(){}
public static Singleton01 getInstance(){
return INSTANCE01;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()-> System.out.println(Singleton01.getInstance())).start();
}
}
}
2 懒汉模式
特点:类使用时才进行实例化
缺点:多线程下,存在线程安全问题
package singleton;
/**
* @author yq
*/
public class Singleton02 {
private static Singleton02 INSTANCE02;
private Singleton02(){}
public static Singleton02 getInstance() {
if(INSTANCE02==null){
// 由于未加锁,多个线程都可以到达if内部,从而产生多个实例
// 模拟线程安全问题 sleep时间越长越容易看到效果
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
INSTANCE02=new Singleton02();
}
return INSTANCE02;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()-> System.out.println(Singleton02.getInstance().hashCode())).start();
}
}
}
3 双重校验锁
特点:双重校验保证大部分时间线程安全
缺点:不可以防止反序列化来获取多个实例
public class Singleton03 {
//此处的volatile必须存在,否则会存在线程安全问题
private static volatile Singleton03 INSTANCE03;
private Singleton03(){}
public static Singleton03 getInstance() {
//A处:第一重判断,减少锁竞争
if(INSTANCE03==null){
synchronized (Singleton03.class){
//B处:第二重判断,校验其他线程是否已经产生了实例,避免线程安全问题
if(INSTANCE03==null){
INSTANCE03=new Singleton03();
}
}
}
return INSTANCE03;
}
public static void main(String[] args) {
for (;;) {
new Thread(()-> System.out.println(Singleton03.getInstance().hashCode())).start();
}
}
}
4 静态内部类
特点:比较完美的写法,但是对于需要传参的实例不友好,因为实例是在内部类创建的,外部传参很困难
public class Singleton04 {
private Singleton04(){}
private static class Holder{
private final static Singleton04 INSTANCE =new Singleton04();
}
public static Singleton04 getInstance(){
return Holder.INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()-> System.out.println(Singleton04.getInstance().hashCode())).start();
}
}
}
5 枚举单例
特点:Effective Java 中推荐的单例写法,非常完美.可以保持单例,线程安全,还可以防止反序列化破解
public enum Singleton05 {
INSTANCE;
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> System.out.println(INSTANCE.hashCode())).start();
}
}
}