设计模式应用场景
只需要存在一个实例
实现方式
1.饿汉式
类加载到内存后,就实例化一个单例,JVM保证线程安全
public class M1 {
private static final M1 INSTANCE = new M1();
private M1(){}
public static M1 getInstance(){
return INSTANCE;
}
public static void main(String[] args) {
M1 m1 = M1.getInstance();
M1 m2 = M1.getInstance();
System.out.println(m1==m2);
}
}
public class M2 {
private static final M2 INSTANCE;
static {
INSTANCE = new M2();
}
private M2() {};
public static M2 getInstance() {
return INSTANCE;
}
public static void main(String[] args) {
M2 m1 = M2.getInstance();
M2 m2 = M2.getInstance();
System.out.println(m1 == m2);//输出结果为true
}
}
2.懒汉式
lazy loading 也称懒汉式
虽然达到了按需初始化的目的,但却带来线程不安全的问题
public class M3 {
private static M3 INSTANCE;
private M3() {
}
public static M3 getInstance() {
if (INSTANCE == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new M3();
}
return INSTANCE;
}
public static void main(String[] args) {
for(int i=0; i<100; i++) {
//输出不同的Hash值
new Thread(()->System.out.println(M3.getInstance().hashCode())).start();
}
}
}
可以通过synchronized解决,但效率会下降
public class M4 {
private static M4 INSTANCE;
private M4() {
}
public static synchronized M4 getInstance() {
if (INSTANCE == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new M4();
}
return INSTANCE;
}
public static void main(String[] args) {
for(int i=0; i<100; i++) {
//输出相同的Hash值
new Thread(()->System.out.println(M4.getInstance().hashCode())).start();
}
}
}
3.双重校验锁
保证了线程安全
第一次校验可以防止实例化后多线程争抢锁浪费时间
第二次可以保证安全性让对象单例
public class M5 {
private static volatile M5 INSTANCE;
private M5() {
}
public static M5 getInstance() {
if (INSTANCE == null) {
//双重检查
synchronized (M5.class) {
if(INSTANCE == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new M6();
}
}
}
return INSTANCE;
}
public static void main(String[] args) {
for(int i=0; i<100; i++) {
//输出相同的Hash值
new Thread(()->System.out.println(M5.getInstance().hashCode())).start();
}
}
}
4.静态内部类
外部类被加载时,不会去加载这个类的内部类,这样可以实现懒加载
JVM保证单例
public class M6 {
private M6() {
}
private static class M6Holder {
private final static M6 INSTANCE = new M6();
}
public static M6 getInstance() {
return M6Holder.INSTANCE;
}
public static void main(String[] args) {
for(int i=0; i<100; i++) {
//输出相同的Hash值
new Thread(()->System.out.println(M6.getInstance().hashCode())).start();
}
}
}
5.枚举法
完美的解决方法,懒加载的同时不仅可以解决线程同步,还可以防止反序列化。
public enum M7 {
INSTANCE;
public void m() {}
public static void main(String[] args) {
for(int i=0; i<100; i++) {
//输出相同的Hash值
new Thread(()->System.out.println(M7.INSTANCE.hashCode())).start();
}
}
}