传统的单例模式
- 代码实现:
/** * 单例模式:传统方式实现饿汉式 */ class EHan{ //构造方法 //无参构造私有化,目的为了防止用户创建对象的时候,会产生多个对象! //为了不让直接通过构造方法创建该类对象 private EHan() { } //成员位置声明变量 //私有化,并且使用静态修饰符 private static EHan eHan=new EHan(); public static EHan getEHan(){ return eHan; } } public class Main { public static void main(String[] args) { EHan eHan1=EHan.getEHan(); EHan eHan2=EHan.getEHan(); System.out.println(eHan1==eHan2); System.out.println(eHan1); System.out.println(eHan2); } }
运行结果:
但是传统的单例模式会有安全问题
- 举例如下:
/** * 单例模式:传统方式实现懒汉式 */ class LHan{ //构造方法 public LHan() { } //成员位置声明变量 //私有化,并且使用静态修饰符 private static LHan lHan=null; public static LHan getLHan(){ if (lHan==null){ lHan = new LHan(); } return lHan; } } public class Main { public static void main(String[] args) { ExecutorService pool=Executors.newFixedThreadPool(10); for (int i = 0; i< 10; i++) { pool.execute(new Runnable() { @Override public void run() { System.out.println(LHan.getLHan()); } }); } pool.shutdown(); } }
- 运行结果:红框的位置全限定名@后边的hashCode和其他不同
线程不安全可以使用synchronized同步代码块或者同步函数解决
- 举例如下:
/** * 单例模式:传统方式实现懒汉式 */ class LHan{ //构造方法 public LHan() { } //成员位置声明变量 //私有化,并且使用静态修饰符 private static LHan lHan=null; public synchronized static LHan getLHan(){ if (lHan==null){ lHan = new LHan(); } return lHan; } } public class Main { public static void main(String[] args) { ExecutorService pool=Executors.newFixedThreadPool(10); for (int i = 0; i< 10; i++) { pool.execute(new Runnable() { @Override public void run() { System.out.println(LHan.getLHan()); } }); } pool.shutdown(); } }
- 运行结果:
这样做效率低下:使用双重判断解决
- 举例如下:
/** * 单例模式:传统方式实现饿汉式 */ class LHan{ //构造方法 public LHan() { } //成员位置声明变量 //私有化,并且使用静态修饰符 private static LHan lHan=new LHan(); public static LHan getLHan(){ if (lHan==null){ synchronized (LHan.class){ if (lHan==null){ lHan = new LHan(); } } } return lHan; } } public class Main { public static void main(String[] args) { ExecutorService pool=Executors.newFixedThreadPool(10); for (int i = 0; i< 10; i++) { pool.execute(new Runnable() { @Override public void run() { System.out.println(LHan.getLHan()); } }); } pool.shutdown(); } }
- 运行结果:
两种安全方式的时间比较
- 分别在上述的两个代码的main方法开始以及结束加上
- 当线程池中有100个线程,并且循环100次的时候
- 使用同步的方式时间间隔:21毫秒
- 使用双重判断的时间间隔:14毫秒
- 从这个方面可以说明双重判断的效率要优于同步代码块的效率