单例模式常见的实现方式有懒汉式和饿汉式两种(不知道谁发明的这糙名字emm...)直接贴代码如下:

 1.Eagerly initialized

 1 /**
 2 * 不可变类其实例不能被修改; 不可变对象,本质上是线程安全的; 
 3 */
 4 public final class EagerlyMan {
 5     // 将域定义为final,可以确保对实例的引用在线程传递中的不可变
 6     private static final EagerlyMan INSTANCE = new EagerlyMan();
 7 
 8     private EagerlyMan() {}
 9 
10     // 使用静态工厂取代导出公有的静态final域,可以提供更多的灵活性
11     public static EagerlyMan getInstance() { return INSTANCE; }
12 
13 }

1. static 关键字可以起到两个重要作用: 

①、方便类在没有创建对象的情况下调用方法或字段(必须是static的);

②、被static修饰的字段或代码块当且只当类加载的时候就初始化,static字段在内存中只有一个副本,被所有的对象共享

2. Lazily initialized需要同步机制

 1 public final class LazyMan {
 2 
 3     private static LazyMan instance;
 4 
 5     private LazyMan() {
 6         if (instance != null) { 
 7         // 享有特权的客户端可以调用AssessibleObject.setAccessinle方法通过反射调用私有构造器,为抵御这种方式可以抛出一个异常
 8             throw new IllegalStatExcaption();
 9         }
10     }
11   // 避免延迟初始化产生的竞态条件,使用同步代码块保证原子性和可见性
12     public static synchronized LazyMan getInstance() {
13         if (instance == null) {
14             instance = new LazyMan();
15         }
16         return instance;
17     }
18 }

在这两种习惯模式中(正常初始化和延迟初始化)应用到非静态域上也是一样的

3. 双重检查初始化

 1 public final class SuperLazyMan {
 2 
 3     private volatile SuperLazyMan instance;
 4 
 5     private SuperLazyMan() {
 6         if (instance != null) {
 7             throw new IllegalStateException();
 8         }
 9     }
10 
11     public SuperLazyMan getInstance() {
12          // 线程私有的局部变量, 确保实例域被初始化后只读取一次,可以提升性能
13         SuperLazyMan result = instance; 
14 
15         if (result == null) {
16             synchronized (this) {
17                 result = instance;
18                 if (result == null) {
19                     /** 这段是非原子操作,大致做了3件事情: 
20                     * 1.给实例分配内存 2.构造器初始化 3.将instance对象指向分配的内存空间
21                     * 由于指令的重排序,3可能会在2之前执行, 因此instance必须加上volatile,禁止指令重排序
22                     */
23                     result = instance = new SuperLazyMan();
24                 }
25             }
26         }
27         return result;
28     }
29 }

4.使用内部类延迟初始化

 1 public final class DemandHolderLazyMan {
 2 
 3     private DemandHolderLazyMan() {}
 4     
 5     public static DemandHolderLazyMan getInstance() {
 6         return HelperHoler.INSTANCE;
 7     }
 8 
 9     private static class HelperHoler {
10 
11         private static final DemandHolderLazyMan INSTANCE = new DemandHolderLazyMan();
12     }
13 }

 总结:

大多数的域都应该正常地进行初始化,而不是延迟初始化。如果是为了达到性能目标而必须延迟初始化一个域,对于实例域,就使用双重检查模式;对于静态域,则使用内部类延迟初始化。

                                       —————— 摘自Joshua Bloch "Effective Java, Second Edition"

posted on 2018-08-26 11:22  lix_y  阅读(130)  评论(0编辑  收藏  举报