zoukankan      html  css  js  c++  java
  • 单例模式并不简单

    单例模式常见的实现方式有懒汉式和饿汉式两种(不知道谁发明的这糙名字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"

  • 相关阅读:
    CSS 选择器及各样式引用方式
    The usage of docker image wurstmeister/kafka
    kafka connect rest api
    Kafka connect in practice(3): distributed mode mysql binlog ->kafka->hive
    jail-break-rule
    Ubuntu下把缺省的dash shell修改为bash shell
    mysql 5.7 enable binlog
    Docker CMD in detail
    记一次深度系统安装至windows系统盘提示挂载为只读模式问题
    android 监听声音变化
  • 原文地址:https://www.cnblogs.com/lix-y/p/9536001.html
Copyright © 2011-2022 走看看