设计模式一单例模式

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


单例(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 }
原文地址:https://www.cnblogs.com/loveer/p/11287148.html