单例模式

  概述

  单例模式是Java中常见的设计模式,常见的单例模式有三种实现方式:懒汉式,饿汉式,改进型的懒汉式。

  单例模式的特点是:

   (1)单例模式只能有一个实例,并且不能被外部实例化

   (2)单例模式必须自己创建自己的唯一实例

   (3)单例模式必须对其他对象共享自己的唯一实例

  懒汉式实现

package com.jyk.singleton.test;

public class HungrySingle {
    
    private static HungrySingle hungrySingle = null;

    private HungrySingle() {}
    
    public static HungrySingle getInstance(){
        if(hungrySingle == null){
            return new HungrySingle();
        }
        return hungrySingle;
    }
}

  饿汉式实现

package com.jyk.singleton.test;

public class LazySingle {
    
    private LazySingle(){}

    private static LazySingle lazySingle = new LazySingle();
    
    public static LazySingle getInstance(){
        return lazySingle;
    }
}

  懒汉式和饿汉式区别

  懒汉式和饿汉式模式都是单例模式的实现。

  饿汉式是线程安全的,因为在类初始化的时候便已经创建了对象,此后getInstance时,只需将创建好的对象返回即可,但该实现会导致类创建时便创建对象,而且是static的修饰的,所以会一直保存在内存中。

  而懒汉式不是线程安全的,假设两个线程A,B同时进入了getInstance,A走完if判断出来此时对象还创建,此时B进到判断条件后对象仍为null,所以可能会导致两个实例的创建。但懒汉式相比饿汉式的优点是,对象在使用时才创建,不会在类创建时便跟着产生,从而减少了内存堆的压力。

  改进型懒汉式:通过同步操作加上双重判定保证线程安全

  (1)在getInstance方法上添加同步操作

package com.jyk.singleton.test;

public class ReformSingle {

    private ReformSingle(){}
    
    private static ReformSingle reformSingle = null;
    
    public static synchronized ReformSingle getInstance(){
        if(reformSingle == null){
            return new ReformSingle();
        }
        return reformSingle;
    }
}

  (2)双重检查锁定

package com.jyk.singleton.test;

public class ReformSingle {

    private ReformSingle(){}
    
    private static ReformSingle reformSingle = null;
    
    public static ReformSingle getInstance(){
        if(reformSingle == null){
            synchronized (ReformSingle.class) {
                if(reformSingle == null){
                    return new ReformSingle();
                }
            }
        }
        return reformSingle;
    }
}

  懒汉式再优化:volatile关键字

package com.jyk.singleton.test;

public class ReformSingle {

    private ReformSingle(){}
    
    private static volatile ReformSingle reformSingle = null;
    
    public static ReformSingle getInstance(){
        if(reformSingle == null){
            synchronized (ReformSingle.class) {
                if(reformSingle == null){
                    return new ReformSingle();
                }
            }
        }
        return reformSingle;
    }
}

  为什么要做如此优化呢?如上未添加 volatile关键字的双重检验单例模式的实现,是存在风险的,分析如下:

  (1)线程A,B同时进入了getInstance方法,此时A判断对象为null,进入同步块

  (2)当A部分实例化对象时,B判断不为空,直接返回一个部分实例化的对象

  添加volatile后,当A实例化对象时,B线程会等到该对象对应的主存地址更新后,再读取最新的值。

  备注:volatile关键字原理详见博客中Java常见面试题版块。

  补充:什么是线程安全

  如果你的程序是一个进程中运行的,而该进程中跑着多个线程,并且可能同时操作该部分相同的程序,若每次运行的结果和单线程执行的结果相同,并且变量值和预期的结果也是相同的,那么称之为线程安全。

  

 

  

  

posted @ 2017-04-12 00:31  纪煜楷  阅读(340)  评论(0编辑  收藏  举报