zoukankan      html  css  js  c++  java
  • 设计模式一单例模式

    创建型模式
      主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”


    单例(Singleton)模式:
      某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,
      其拓展是有限多例模式。

    原型(Prototype)模式:
      将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
    工厂方法(FactoryMethod)模式:
      定义一个用于创建产品的接口,由子类决定生产什么产品。
    抽象工厂(AbstractFactory)模式:
      提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
    建造者(Builder)模式:
      将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,
      最后构建成该复杂对象。



    一.使用synchronized实现单例模式
      实现Serializable ,被transient修饰,不能被序列化
    一个静态变量不管是否被transient修饰,均不能被序列化

      volatile是变量修饰符,其修饰的变量具有可见性
    可见性也就是说一旦某个线程修改了该被volatile修饰的变量,
        它会保证修改的值会立即被更新到主存,

    当有其他线程需要读取时,可以立即获取修改之后的值。
    volatile禁止指令重排

    synchronized可作用于一段代码或方法,既可以保证可见性,又能够保证原子性。

    单例模式双重校验锁使用synchronized,为什么同时使用volatile?
      synchronized虽然保证了原子性,但却没有保证指令重排序的正确性,
      会出现A线程执行初始化,但可能因为构造函数里面的操作太多了,
      所以A线程的Instance实例还没有造出来,但已经被赋值了。
      而B线程这时过来了,错以为Instance已经被实例化出来,
       一用才发现Instance尚未被初始化。

      要知道我们的线程虽然可以保证原子性,但程序可能是在多核CPU上执行。

    《指令重排》

    当instance不为null时,仍可能指向一个"被部分初始化的对象"

    问题出在这行简单的赋值语句:instance = new Singleton();

    它并不是一个原子操作。事实上,它可以”抽象“为下面几条JVM指令:

    memory = allocate();    //1:分配对象的内存空间
    initInstance(memory);   //2:初始化对象
    instance = memory;      //3:设置instance指向刚分配的内存地址
    
    

    上面操作2依赖于操作1,但是操作3并不依赖于操作2

    所以JVM可以以“优化”为目的对它们进行重排序,经过重排序后如下:

    memory = allocate();    //1:分配对象的内存空间
    instance = memory;      //3:设置instance指向刚分配的内存地址(此时对象还未初始化)
    ctorInstance(memory);   //2:初始化对象
     1 class Singleton1 implements Serializable {
     2 
     3     private static volatile Singleton1 INSTANCE = null;
     4 
     5     private Singleton1() {
     6         //解决反射问题
     7         if (INSTANCE != null) {
     8             throw new RuntimeException("已存在实例");
     9         }
    10     }
    11 
    12     //防止序列化攻击,
    13     //当JVM从内存中反序列化地"组装"一个新对象时,
    14     // 就会自动调用这个 readResolve方法来返回我们指定好的对象了,单例规则也就得到了保证。
    15     private Object readResolve() {
    16         return INSTANCE;
    17     }
    18 
    19     public static Singleton1 getInstance() {
    20         //双重检验锁模式
    21         if (INSTANCE == null) {              //Single Checked
    22             synchronized (Singleton1.class) {
    23                 if (INSTANCE == null)       //Double Checked
    24                     INSTANCE = new Singleton1();
    25             }
    26         }
    27         return INSTANCE;
    28     }
    29 }

    二.使用静态内部类实现单例模式

    1.调用外部类的静态变量和静态方法可以让外部类被加载到内存中,
    不过被调用的外部类的内部类(不论是否静态)不会被加载。
    2.加载静态内部类之前会先加载外部类,静态内部类或非静态内部类在使用它们的成员时才加载。
    3.内部类可以随意使用外部类的成员对象和方法(即使私有),而不用生成外部类对象

    final修饰的方法是不能被重写的,但是可以重载。
     1 class Singleton2 {
     2 
     3     private static class SingletonCreater {
     4         //由于实例被声明为静态和final变量了,在第一次加载类在内存中就会初始化,所以创建实例本身是线程安全的
     5         private static final Singleton2 INSTANCE = new Singleton2();
     6     }
     7 
     8     private Singleton2() {
     9     }
    10 
    11     public static final Singleton2 getInstance() {
    12         //当线程调用getInstance方法,由于调用了静态内部类的成员,会使内部类被加载到内存,
    13         //而内部类的成员此时也被加载并初始化了,这样返回的就是外部类的实例了
    14         return SingletonCreater.INSTANCE;
    15     }
    16 }

    三.枚举实现单例模式
    1.避免序列化问题
    2.避免反射攻击
     1 enum Singleton3 {
     2     INSTANCE;
     3 
     4     public void printHello() {
     5         System.out.println("Hello");
     6     }
     7 
     8     public static void main(String[] args) throws Exception {
     9         Singleton3 instance = Singleton3.INSTANCE;
    10         instance.printHello();
    11     }
    12 }
  • 相关阅读:
    spring mvc全局异常处理
    spring mvc URL忽略大小写
    easyui datagrid 加载两次请求,触发两次ajax 请求 问题
    根据多列去除重复记录,保留具有特殊列值的行,去除其他行
    ibatis 批量插入oracle总结
    ibatis 参数错误,无效字符
    jvm 原理和优化
    tomcat 内存问题 xms xmx permsize maxPermsize
    java 中与 或 非 异或 和位移运算
    项目jar包管理,使用 .userlibraries 文件增加jar包的可移植性,明确jar包依赖,多项目共用jar包里
  • 原文地址:https://www.cnblogs.com/loveer/p/11287148.html
Copyright © 2011-2022 走看看