java单例-积木系列

时间:2022-10-25 20:07:21
一步步知识点归纳吧,把以前似懂非懂,了解表面,知道点不知道面的知识归一下档。
 
懒汉式单例:
私有化构造函数,阻止外界实例话对象,调用getInstance静态方法,判断是否已经实例化。
为什么是懒汉,因为它是属于延迟加载这个实例的,也就是说不用到的时候,不实例化对象的。
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两次,保证在进入同步块的线程不产生新的实例。

当多个线程并发时都判断出实例为null则开始竞争锁,其中一个线程拿到锁去实例化操作,而其他线程需要等待,而实例化好的线程释放所后,后进入的线程如果不判断单例是否已经产生,那么就会又去实例化一个对象,如此就不能实现单例了。
如此双重检验锁开启来完美,而指令重排序会引起问题。我想这也是一个学习重排序的好例子。
instance = new Singleton();

上面这个代码不是一个原子操作,即无法被翻译成一个指令完成。

它是由一下3个指令完成的:
给 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;
}
volatile关键字事实上是对编译时的重排序进行了屏障。具体各家说法可以阅读下面的文章:

扩展阅读

由上可以感受到,在加载时就初始化好实例,会有很多需要考虑的东西,那么如果在编译阶段就实例化好,如此就可以避免并发带来的问题。

那就是所谓的饿汉式单例:

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;
}

我们来看看如何破坏单例:

1,序列化与反序列化
当然我们前面写的代码不需要序列化和反序列化,就没有这个问题了,只是说送这个方面我们考虑如何破坏它,参考如下例子:
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单例-积木系列的更多相关文章

  1. java单例的几种实现方法

    java单例的几种实现方法: 方式1: public class Something { private Something() {} private static class LazyHolder ...

  2. java单例类&sol;

    java单例类  一个类只能创建一个实例,那么这个类就是一个单例类 可以重写toString方法 输出想要输出的内容 可以重写equcal来比较想要比较的内容是否相等 对于final修饰的成员变量 一 ...

  3. Java单例类的简单实现

    对于java新手来说,单例类给我的印象挺深,之前一道web后台笔试题就是写单例类.*.*可惜当时不了解. 在大部分时候,我们将类的构造器定义成public访问权限,允许任何类*创建该类的对象.但在某 ...

  4. 【Java学习笔记之三十】详解Java单例&lpar;Singleton&rpar;模式

    概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...

  5. 转:java单例设计模式

    本文转自:http://www.cnblogs.com/yinxiaoqiexuxing/p/5605338.html 单例设计模式 Singleton是一种创建型模式,指某个类采用Singleton ...

  6. 熟悉的味道——从Java单例写到C&plus;&plus;单例

    设计模式中,单例模式是常见的一种.单例模式需要满足以下两个条件: 保证一个类只能创建一个示例: 提供对该实例的全局访问点. 关于单例最经典的问题就是DCL(Double-Checked Lock),今 ...

  7. java单例五种实现模式梳理

    java单例五种实现模式 饿汉式(线程安全,调用效率高,但是不能延时加载) 一上来就把单例对象创建出来了,要用的时候直接返回即可,这种可以说是单例模式中最简单的一种实现方式.但是问题也比较明显.单例在 ...

  8. JAVA单例实现方式(常用)

    JAVA单例实现方式(常用) public class Singleton { // Q1:为什么要使用volatile关键字? private volatile static Singleton u ...

  9. Java单例设计模式的实现

    1. 单例设计模式的定义 单例设计模式确保类只有一个实例对象,类本身负责创建自己的对象并向整个系统提供这个实例.在访问这个对象的时候,访问者可以直接获取到这个唯一对象而不必由访问者进行实例化. 单例设 ...

随机推荐

  1. 【实践】js 如何实现动态添加文本节点

    对于我这个js 小白来说 今天鼓起勇气做起了邮箱认证这个特效 但是这次不是想说如何实现这这个特效而是想记录一下特效当中的某个部分 那就是向元素节点动态添加文本节点 百度了一下动态添加文本节点的方式 是 ...

  2. 黄学长模拟day1 球的序列

    N个编号为1-n的球,每个球都有唯一的编号.这些球被排成两种序列,分别为A.B序列,现在需要重新寻找一个球的序列l,对于这个子序列l中任意的两个球,要求j,k(j<k),都要求满足lj在A中位置 ...

  3. js判断是否为ie6以外的浏览器,若是,则调用相应脚本

    if(navigator.userAgent.indexOf("MSIE 6.0") < 0) { //相应JavaScript脚本}

  4. eclipse项目编码问题

    使得eclipse的新建项目的默认编码直接为UTF-8: 在菜单栏的Window->Preferences->General->Workspace->Text file enc ...

  5. php大力力 &lbrack;017节&rsqb;来来来,庆祝一下&&num;127873&semi;大力力第一个数据库录入程序完成!

    庆祝一下

  6. Lucene&sol;ElasticSearch 学习系列 &lpar;1&rpar; 为什么学,学什么,怎么学

    为什么学 <What I wish I knew When I was 20>这本书给了我很多启发.作者在书中提到,Stanford 大学培养人才的目标是 ”T形人才“:精通某个领域,但对 ...

  7. Linux内核:sk&lowbar;buff解析

    sk_buff 目录 1 sk_buff介绍 2 sk_buff组成 3 struct sk_buff 结构体 4 sk_buff成员变量 4.1 Layout布局 4.2 General通用 4.3 ...

  8. 阿里安全潘多拉实验室首先完美越狱苹果iOS 11&period;2

    苹果官方对iOS 11的评价是"为iPhone带来巨大进步,让iPad实现里程碑式飞跃."但为了不断修复Bug,苹果于12月2日推出最新的iOS 11.2,修复了Google安全人 ...

  9. delphi弹出信息框大全

    1. 警告信息框 MessageBox(Handle,'警告信息框','警告信息框',MB_ICONWARNING);2.疑问信息框 MessageBox(Handle,'疑问信息框','疑问信息框' ...

  10. 让anujs支持rc-select

    git clone git@github.com:react-component/select.git cd select npm i babel-plugin-antd --save-dev npm ...