java单例-积木系列
public class Singleton {
   private static Singleton instance;
   private Singleton (){}
   public static Singleton getInstance() {
      if (instance == null) {
         instance = new Singleton();
      }
      return instance;
   }
}
线程安全问题:当多线程同时调用getInstance()方法,同时判断出instance,进入实例化操作,单利就不复存在。
为了线程安全,那我们对getInstance()方法进行同步化:
public class Singleton {
  private static Singleton instance;
  private Singleton (){}
  public static synchronized Singleton getInstance() {
    if (instance == null) {
        instance = new Singleton();
    }
    return instance;
  }
}
synchronized修饰保证同一时间只有一个线程可以执行getInstance方法,如此可以保证单例的线程安全。但是同步粒度似乎太大,事实上当实例初始化后我们并不需保证一个个线程排队来取这个实例。
public class Singleton {
  private static Singleton instance;
  private Singleton (){}
  public static Singleton getSingleton() {
      if (instance == null) {                        //Single Checked
          synchronized (this) {
              if (instance == null) {                //Double Checked
                  instance = new Singleton();
              }
          }
      }
      return instance ;
  }
}
同步快外面和里面check两次,保证在进入同步块的线程不产生新的实例。
instance = new Singleton();
上面这个代码不是一个原子操作,即无法被翻译成一个指令完成。
给 instance 分配内存
调用 Singleton 的构造函数来初始化成员变量
将instance对象指向分配的内存空间地址
JVM编译时进行指令重排序可能打乱上面三个指令的执行顺序,也就是说可能先直行来1,3然后执行2。那么有这么一种情况当执行好1和3,instance不为null,新进入的线程在判断第一个null时就会直接返回一个没有执行2步骤的实例,如此就有不符合期望了。这的确是个经典的场景。
如果我们在实例初始化后,将第三步,分开写,似乎可以解决这个问题,代码如下:
public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                Singleton temp = instance;
                if (temp == null) {
                    synchronized (Singleton.class) {
                        temp = new Singleton();
                    }
                    instance = temp;
                }
            }
        }
        return instance;
    }
由上可以感受到,在加载时就初始化好实例,会有很多需要考虑的东西,那么如果在编译阶段就实例化好,如此就可以避免并发带来的问题。
那就是所谓的饿汉式单例:
public class Singleton{
    //类加载时就初始化
    private static final Singleton instance = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
        return instance;
    }
}
当然这样做自然有自己的弊端,就是这个单例在没有被使用到的时候就已经需要实例化出来,如此就会占用无谓占用内存,如果这个实例初始化复杂占用资源,而实际未必会使用就比较尴尬了。
或者说,这种方式实例化将无法实现依赖外部参数实例化的场景。
还有一种推荐写法:
public class Singleton {
  private static class SingletonHolder {
    private static final Singleton INSTANCE = new Singleton();
  }
  private Singleton (){}
  public static final Singleton getInstance() {
    return SingletonHolder.INSTANCE;
  }
}
还有一种大师推荐的写法,有没有很高大上:
public enum EasySingleton{
    INSTANCE;
}
我们来看看如何破坏单例:
public class Singleton implements Serializable{
    private volatile static Singleton singleton;
    private Singleton (){}
    public static Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
public class SerializableDemo1 {
    //为了便于理解,忽略关闭流操作及删除文件操作。真正编码时千万不要忘记
    //Exception直接抛出
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //Write Obj to file
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempFile"));
        oos.writeObject(Singleton.getSingleton());
        //Read Obj from file
        File file = new File("tempFile");
        ObjectInputStream ois =  new ObjectInputStream(new FileInputStream(file));
        Singleton newInstance = (Singleton) ois.readObject();
        //判断是否是同一个对象
        System.out.println(newInstance == Singleton.getSingleton());
    }
}
//false
2,反射
public class Singleton {
    public static final Singleton INSTANCE = new Singleton();
    private Singleton() {
    }
    public Singleton getInstance() {
        return INSTANCE;
    }
    public static void main(String[] args) throws Exception {
        // 反射机制破坏单例模式
        Class clazz = Singleton.class;
        Constructor c = clazz.getDeclaredConstructor();
        // 反射机制使得private方法可以被访问!!!
        c.setAccessible(true);
        // 判断反射生成的对象与单例对象是否相等
        System.out.println(Singleton.INSTANCE == c.newInstance());
    }
}
破坏单例的原理就是,不走构造函数即可产生实例的方式,因为我们只关闭了构造函数。
至此,对java单例有一个比较全面的认识,牵涉到大量知识点,需要继续挖掘。
-----------------------20170227补充begin-----------------
3,原型模式
看到原型模式的时候,发现clone方法产生对象也是可以绕过构造函数私有化的。
-----------------------20170227补充end-----------------
----------------------- 20170125补充begin-------------------
刚好读spring ioc源码的时候看到这个类:DefaultSingletonBeanRegistry
看到有这么个模式可以学习下:Registry of Singleton 模式
public class Singleton {
    // 注册表,用于注册子类别物件
    private static Map registry = new HashMap();
    private static Singleton instance;
    public static void register(
                 String name, Singleton singleton) {
        registry.put(name, singleton);
    }
    public static Singleton getInstance() {
        if (instance == null) {
            // getEnv表示取得环境变数
            String style = getEnv("style");
            instance = lookup(style);
        }
        return instance;
    }
    protected static Singleton lookup(String name) {
        return (Singleton) registry.get(name);
    }
}
说白了就是创建个容器管理各种不同的单例,便于统一管理,集中单例的代码。
public class SingletonRegistry {
    private static Map<String, Object> registry =
                      new HashMap<String, Object>();
    private SingletonRegistry() {}
    public static Object getInstance(String classname) {
        Object singleton = registry.get(classname);
        if(singleton != null) {
            return singleton;
        }
        try {
            singleton = Class.forName(classname).newInstance();
        }
        catch(Exception e) {
            throw new RuntimeException(e);
        }
        registry.put(classname, singleton);
        return singleton;
    }
}
----------------------- 20170125补充end-------------------
让我们继续前行
----------------------------------------------------------------------
努力不一定成功,但不努力肯定不会成功。
java单例-积木系列的更多相关文章
- java单例的几种实现方法
		
java单例的几种实现方法: 方式1: public class Something { private Something() {} private static class LazyHolder ...
 - java单例类/
		
java单例类 一个类只能创建一个实例,那么这个类就是一个单例类 可以重写toString方法 输出想要输出的内容 可以重写equcal来比较想要比较的内容是否相等 对于final修饰的成员变量 一 ...
 - Java单例类的简单实现
		
对于java新手来说,单例类给我的印象挺深,之前一道web后台笔试题就是写单例类.*.*可惜当时不了解. 在大部分时候,我们将类的构造器定义成public访问权限,允许任何类自由创建该类的对象.但在某 ...
 - 【Java学习笔记之三十】详解Java单例(Singleton)模式
		
概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...
 - 转:java单例设计模式
		
本文转自:http://www.cnblogs.com/yinxiaoqiexuxing/p/5605338.html 单例设计模式 Singleton是一种创建型模式,指某个类采用Singleton ...
 - 熟悉的味道——从Java单例写到C++单例
		
设计模式中,单例模式是常见的一种.单例模式需要满足以下两个条件: 保证一个类只能创建一个示例: 提供对该实例的全局访问点. 关于单例最经典的问题就是DCL(Double-Checked Lock),今 ...
 - java单例五种实现模式梳理
		
java单例五种实现模式 饿汉式(线程安全,调用效率高,但是不能延时加载) 一上来就把单例对象创建出来了,要用的时候直接返回即可,这种可以说是单例模式中最简单的一种实现方式.但是问题也比较明显.单例在 ...
 - JAVA单例实现方式(常用)
		
JAVA单例实现方式(常用) public class Singleton { // Q1:为什么要使用volatile关键字? private volatile static Singleton u ...
 - Java单例设计模式的实现
		
1. 单例设计模式的定义 单例设计模式确保类只有一个实例对象,类本身负责创建自己的对象并向整个系统提供这个实例.在访问这个对象的时候,访问者可以直接获取到这个唯一对象而不必由访问者进行实例化. 单例设 ...
 
随机推荐
- Oracle中Merge into用法总结
			
MERGE语句是Oracle9i新增的语法,用来合并UPDATE和INSERT语句.通过MERGE语句,根据一张表或子查询的连接条件对另外一张表进行查询,连接条件匹配上的进行UPDATE,无法匹配的执 ...
 - USACO Milking Cows
			
思路: 脑抽了,一看题目,这不就是线段树么,离散化区间合并..最终发现我并不会写...于是看了下题目范围10^6...模拟水之..每个区间左端点+1,右端点-1,从左到右扫一下就行了... 代码: / ...
 - 杭电--1102--Constructing Roads--并查集
			
Constructing Roads Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Other ...
 - Whole life
			
Whole life the wonder such you bring the beautyI can see but i keep deep inside on itOh life i feel ...
 - seleniu+python+chrome的调试
			
1.下载chrome,下载chrome driver,下载地址:http://code.google.com/p/chromedriver/downloads/list 2.安装chrome,默认路径 ...
 - C语言-预估校正法求常微分方程
			
#include<stdio.h> #include<math.h> #define n 14 int main(){ double a = 0.0, b = 1.4,h,m= ...
 - 学习git config配置文件
			
设置 git status的颜色. git config --global color.status auto 一.Git已经在你的系统中了,你会做一些事情来客户化你的Git环境.你只需要做这些设置一 ...
 - Cairo 下载,测试
			
You need to download the all-in-one bundle available here. You can discover this link yourself by vi ...
 - 关于Spring注解
			
* @author 小郑 1 * @content ejb3注解的API定义在javax.persistence.*包里面. 2 * 注释说明: 3 * @E ...
 - HTML编辑器
			
终于有时间静下来总结一下最近的工作. 第一个就是html编辑器: 首先是编辑器的选择,之前用的是ewebeditor,功能很强大,出于粘贴word内容得安装插件的原因,暂时放弃. ewebeditor ...