ps:单例模式有很多种实现方法,以下介绍5种常见的使用方法和一种《Effective Java》中提过的一种实现方法,但是不常用。
1、饿汉式。缺点:不管有没有使用,在类装载时就已经加载内存中。
/*
饿汉式
缺点:不管用到与否,类装载时就完成实例化
*/
public class Mgr01 {
//在Mgr01被类加载器load到内存中就调用私有构造方法实例化(迫不及待,所以称为饿汉)
private static final Mgr01 INSTANCE = new Mgr01();
/*
构造方法私有化,在外部无法调用
*/
private Mgr01(){}
public static Mgr01 getMgro1() {
return INSTANCE;
}
public static void main(String[] args) {
Mgr01 mgr01 = Mgr01.getMgro1();
Mgr01 mgr02 = Mgr01.getMgro1();
System.out.println(mgr01==mgr02);
}
}
2、懒汉式。缺点:虽然可以按需加载,多线程并发的时候,容易产生多个实例。
/*
懒汉式
缺点:虽然可以按需加载,但是多线程并发的时候,容易创建多个实例
*/
public class Mgr02 {
private static Mgr02 INSTANCE;
private Mgr02(){}
//在需要的时候,再进行获取单例对象(所以叫做懒汉式)
public static Mgr02 getMgro1() {
//如果第一次获取,那我们需要进行实例化
if (INSTANCE==null){
//当多线程并发的话,可能有多条线程执行到此部分,所以获得到不同的实例对象
try {
Thread.sleep(1000);
}catch (Exception e){
}
INSTANCE = new Mgr02();
}
return INSTANCE;
}
public static void main(String[] args) {
//多线程测试
for (int i = 0; i < 100; i++) {
//lamda表达式
new Thread(()->{
System.out.println(Mgr02.getMgro1().hashCode());
}).start();
}
}
}
3、懒汉式1.0版本。缺点:解决了多线程并发获取的实例不为单例的问题,但是执行效率较低。
/*
懒汉式
方法上加synchronized锁,但是多线程时候的执行效率较低
*/
public class Mgr03 {
private static Mgr03 INSTANCE;
private Mgr03(){}
//方法上加同步锁,保证此方法每次只能让一个线程使用
public synchronized static Mgr03 getMgro1() {
if (INSTANCE==null){
try {
Thread.sleep(1000);
}catch (Exception e){
}
INSTANCE = new Mgr03();
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
System.out.println(Mgr03.getMgro1().hashCode());
}).start();
}
}
4、懒汉式2.0版本(双重检测锁,可以说是比较完美的。但是写的比较复杂)
/*
懒汉式
不在方法上加synchronized锁,而是
*/
public class Mgr05 {
private static volatile Mgr05 INSTANCE;
private Mgr05(){}
public static Mgr05 getMgro1() {
//双重检测锁
if (INSTANCE==null){
synchronized (Mgr05.class){
//如果不加此判断,那么在上面第一个if判断语句,可能同时进来两个线程,就算锁住这部分代码块,依然会产生不同的实例
if(INSTANCE==null){
try {
Thread.sleep(1000);
}catch (Exception e){
}
INSTANCE = new Mgr05();
}
}
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
System.out.println(Mgr05.getMgro1().hashCode());
}).start();
}
}
5、静态内部类方式(完美)。
/*
静态内部类方式
JVM保证单例
加载外部类的时候不会加载内部类,这样可以实现需要的时候再加载
*/
public class Mgr06 {
private Mgr06(){}
//内部类,用来获取单例
private static class Mgr06Holder{
private final static Mgr06 INSTANSE = new Mgr06();
}
public synchronized static Mgr06 getMgro1() {
return Mgr06Holder.INSTANSE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
System.out.println(Mgr06.getMgro1().hashCode());
}).start();
}
}
}
5、使用枚举实现(代码简洁,保证单例,而且可以防止反序列化)
public enum Mgr07 {
INSTANCE;
public void m(){}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
System.out.println(Mgr07.INSTANCE.hashCode());
}).start();
}
}
}
总结:推荐使用第1种和第5种实现方法,简单优雅。