根据单例对象的初始化时间分为两类
1、饿汉
饿汉单例模式在类加载时就初始化创建单例对象
实现方式:在声明变量时就初始化
public class Hungry {
private static Hungry INSTANCE=new Hungry();
//饿汉:在加载类时就初始化单例对象
private Hungry(){}
public static Hungry getINSTANCE(){
return INSTANCE;
}
public void d(){
System.out.println("do");
}
public static void main(String[] args) {
Hungry h1= Hungry.getINSTANCE();
Hungry h2=Hungry.getINSTANCE();
h1.d();
System.out.println(h1==h2);
}
}
缺点:加载就初始化对象,可能浪费内存
2、懒汉
类加载时不进行初始化,在使用单例对象时才初始化创建对象
实现方式:使用时判断
public class Lazy {
private static Lazy INSTANCE;
private Lazy(){};
public static Lazy getInstance(){
if(INSTANCE==null) //加载时才初始化对象,线程安全问题,因为if判断和new不是原子的,可能有多i个线程判断完去new线程
INSTANCE = new Lazy();
return INSTANCE;
}
}
缺点:由于访问时才创建对象,不同线程在访问时由于同步问题可能
解决方式:
- 加线程锁判断
public class Syn {
private static Syn INSTANCE;
private Syn(){};
public static Syn getINSTANCE() {
if (INSTANCE == null)
synchronized (Syn.class) {
if (INSTANCE == null)
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Syn();
}
return INSTANCE;
}
}
- 内部类
public class Inner {
private Inner(){}
private class In{
private final static Inner INSTANCE = new Inner();
//在内部类中加载外部的实例,外部线程在加载时不加载内部类,在调用getINSTANCE时才加载内部类,既做到了懒汉加载,又保证了线程安全
}
public static Inner getInstance(){
return In.INSTANCE;
}
}
- 为枚举类定义方法,枚举成员作为对象使用(枚举类的加载方式???)
public enum Enum { //给枚举类定义方法
INSTANCE;
public void m(){}
}