设计模式01-单例模式Singleton
一、饿汉式
在平时工作中,如果用到单例使用这种就足够了,类加载到内存后,就实例化一个单例,JVM保证线程安全。
优点:简单实用,推荐使用!
缺点:不管用到与否,类装载时就完成实例化,吹毛求疵。你说你不用你写他做什么呢,起程序的时候慢点就慢点呗。
代码:
/**
* 饿汉式01
*/
public class Mgr01 {
private static final Mgr01 INSTANCE = new Mgr01();
private Mgr01() {};
public static Mgr01 getInstance() {
return INSTANCE;
}
public static void main(String[] args) {
Mgr01 mgr01 = Mgr01.getInstance();
Mgr01 mgr02 = Mgr01.getInstance();
System.out.println(mgr01 == mgr02);
}
}
第二种写法,通过静态语句块写的本质上和01没有区别。
/**
* 饿汉式02
* 通过静态语句块写的本质上和01没有区别
*/
public class Mgr02 {
private static final Mgr02 INSTANCE;
static {
INSTANCE = new Mgr02();
}
private Mgr02() { };
public static Mgr02 getInstance() {
return INSTANCE;
}
public static void main(String[] args) {
Mgr02 mgr01 = Mgr02.getInstance();
Mgr02 mgr02 = Mgr02.getInstance();
System.out.println(mgr01 == mgr02);
}
}
二、懒汉式 1.0
在使用的时候判断,如果未存在就初始化,已存在就使用现有的,这样就实现了用的时候装载。但是会出现多线程装载多个的情况。
优点:用的时候装载,不想饿汉一开始就装载。
缺点:多线程会有问题。
代码:
/**
* lazy loading
* 也称懒汉式
* 虽然达到了按需初始化的目的,但却带来线程不安全的问题
*/
public class Mgr03 {
private static Mgr03 INSTANCE;
private Mgr03() {
};
public static Mgr03 getInstance() {
if (INSTANCE == null) {
//用来方便测试出问题
//睡1毫米,让线程在判断是==null后,有1毫米的时间让别的线程打断
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Mgr03();
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
System.out.println(getInstance());
}).start();
}
}
}
运行结果
com.zhow.Mgr03@b906e9
com.zhow.Mgr03@3528c9
com.zhow.Mgr03@c39c3d
com.zhow.Mgr03@f075d2
com.zhow.Mgr03@3528c9
com.zhow.Mgr03@f20091
com.zhow.Mgr03@3528c9
com.zhow.Mgr03@107a4d0
com.zhow.Mgr03@c39c3d
com.zhow.Mgr03@174cde1
com.zhow.Mgr03@3528c9
com.zhow.Mgr03@3528c9
com.zhow.Mgr03@184edb
com.zhow.Mgr03@1d68418
com.zhow.Mgr03@1c65632
com.zhow.Mgr03@4b861f
com.zhow.Mgr03@c45a8f
com.zhow.Mgr03@3bf5ac
com.zhow.Mgr03@c4944f
Process finished with exit code 0
三、懒汉式 2.0 synchronized锁对象
在1.0的基础上加synchronized锁,锁定Mgr04.Class,保证线程安全。但是因为加入了synchronized锁会导致效率下降。
优点:线程安全
缺点:加入synchronized锁,每次使用Mgr04对象会先判断锁的状态,导致效率降低。
代码:
public class Mgr04 {
private static Mgr04 INSTANCE;
private Mgr04() {
}
;
public static synchronized Mgr04 getInstance() {
if (INSTANCE == null) {
//用来方便测试出问题
//睡1毫米,让线程在判断是==null后,有1毫米的时间让别的线程打断
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Mgr04();
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(() -> {
System.out.println(getInstance());
}).start();
}
}
}
运行结果:
com.zhow.Mgr04@c45a8f
com.zhow.Mgr04@c45a8f
com.zhow.Mgr04@c45a8f
com.zhow.Mgr04@c45a8f
com.zhow.Mgr04@c45a8f
com.zhow.Mgr04@c45a8f
com.zhow.Mgr04@c45a8f
com.zhow.Mgr04@c45a8f
com.zhow.Mgr04@c45a8f
com.zhow.Mgr04@c45a8f
com.zhow.Mgr04@c45a8f
com.zhow.Mgr04@c45a8f
com.zhow.Mgr04@c45a8f
com.zhow.Mgr04@c45a8f
com.zhow.Mgr04@c45a8f
com.zhow.Mgr04@c45a8f
com.zhow.Mgr04@c45a8f
com.zhow.Mgr04@c45a8f
com.zhow.Mgr04@c45a8f
Process finished with exit code 0
四、懒汉式 2.0 synchronized锁静态代码块
使用synchronized锁静态代码块妄图通过减小同步代码块的方式提高效率,然后不可行。虽然是提高了效率但是由于在释放锁后没有进行判断导致线程不安全。
优点:减小同步代码块,提高效率
缺点:线程不安全
代码:
public class Mgr05 {
private static Mgr05 INSTANCE;
private Mgr05() {
}
;
public static Mgr05 getInstance() {
if (INSTANCE == null) {
//妄图通过减小同步代码块的方式提高效率,然后不可行
synchronized (Mgr05.class) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Mgr05();
}
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(() -> {
System.out.println(getInstance());
}).start();
}
}
}
运行结果:
com.zhow.Mgr05@1a9e02d
com.zhow.Mgr05@1a9e02d
com.zhow.Mgr05@1a9e02d
com.zhow.Mgr05@1a9e02d
com.zhow.Mgr05@1a9e02d
com.zhow.Mgr05@1a9e02d
com.zhow.Mgr05@3bf5ac
com.zhow.Mgr05@3528c9
com.zhow.Mgr05@4b861f
com.zhow.Mgr05@e24daa
com.zhow.Mgr05@1d68418
com.zhow.Mgr05@1c65632
com.zhow.Mgr05@c39c3d
com.zhow.Mgr05@f20091
com.zhow.Mgr05@174cde1
com.zhow.Mgr05@107a4d0
com.zhow.Mgr05@f075d2
com.zhow.Mgr05@4f5086
com.zhow.Mgr05@b906e9
Process finished with exit code 0
五、懒汉式 2.0 synchronized 双重检查
在释放锁后加入判断,保证线程安全
优点:线程安全
缺点:太麻烦了
代码:
public class Mgr06 {
private static volatile Mgr06 INSTANCE;//防止指令重排 JIT
private Mgr06() {
}
;
public static Mgr06 getInstance() {
if (INSTANCE == null) {
//双重检查
synchronized (Mgr06.class) {
if (INSTANCE == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Mgr06();
}
}
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(() -> {
System.out.println(getInstance());
}).start();
}
}
}
运行结果:
com.zhow.Mgr06@184edb
com.zhow.Mgr06@184edb
com.zhow.Mgr06@184edb
com.zhow.Mgr06@184edb
com.zhow.Mgr06@184edb
com.zhow.Mgr06@184edb
com.zhow.Mgr06@184edb
com.zhow.Mgr06@184edb
com.zhow.Mgr06@184edb
com.zhow.Mgr06@184edb
com.zhow.Mgr06@184edb
com.zhow.Mgr06@184edb
com.zhow.Mgr06@184edb
com.zhow.Mgr06@184edb
com.zhow.Mgr06@184edb
com.zhow.Mgr06@184edb
com.zhow.Mgr06@184edb
com.zhow.Mgr06@184edb
com.zhow.Mgr06@184edb
Process finished with exit code 0
六、静态内部类方式 完美
在Mgr07里面定义了一个静态的内部类,静态内部类里面初始化了一个Mgr07。因为Mgr07Holder的INSTANCE的属性是私有的,所以只有Mgr07能访问到。在Mgr07中定义一个getInstance方法返回静态内部类里面的INSTANCE。
装载Mgr07的时候是不会装载Mgr07Holder的,只有调用getInstance的时候才会加载,所以不但实现了懒加载,还实现了线程安全(线程安全是通过JVM的加载Class只加载一次的特性保证的)。
优点:完美
缺点:不是中的完美
代码:
/**
* 静态内部类方式
* JVM保证单例
* 加载外部类时不会加载内部类,这样可以实现懒加载
*/
public class Mgr07 {
private Mgr07() {
}
private static class Mgr07Holder {
private final static Mgr07 INSTANCE = new Mgr07();
}
public static Mgr07 getInstance() {
return Mgr07Holder.INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(() -> {
System.out.println(getInstance());
}).start();
}
}
}
运行结果:
com.zhow.Mgr07@c39c3d
com.zhow.Mgr07@c39c3d
com.zhow.Mgr07@c39c3d
com.zhow.Mgr07@c39c3d
com.zhow.Mgr07@c39c3d
com.zhow.Mgr07@c39c3d
com.zhow.Mgr07@c39c3d
com.zhow.Mgr07@c39c3d
com.zhow.Mgr07@c39c3d
com.zhow.Mgr07@c39c3d
com.zhow.Mgr07@c39c3d
com.zhow.Mgr07@c39c3d
com.zhow.Mgr07@c39c3d
com.zhow.Mgr07@c39c3d
com.zhow.Mgr07@c39c3d
com.zhow.Mgr07@c39c3d
com.zhow.Mgr07@c39c3d
com.zhow.Mgr07@c39c3d
com.zhow.Mgr07@c39c3d
Process finished with exit code 0
七、enum 完美中的完美
不仅可以解决线程同步,还可以防止反序列化。
优点:完美中的完美
缺点:过于完美
代码:
/**
* 不仅可以解决线程同步,还可以防止反序列化。
*/
public enum Mgr08 {
INSTANCE;
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(() -> {
System.out.println(INSTANCE.hashCode());
}).start();
}
}
}
运行结果:
15758802
15758802
15758802
15758802
15758802
15758802
15758802
15758802
15758802
15758802
15758802
15758802
15758802
15758802
15758802
15758802
15758802
15758802
15758802
Process finished with exit code 0