原创

单例模式

HungrySingleton

package com.geely.design.pattern.creational.singleton;

import java.io.Serializable;

/**
 * Created by geely
 */
public class HungrySingleton implements Serializable,Cloneable{

    private final static HungrySingleton hungrySingleton;

    static{
        hungrySingleton = new HungrySingleton();
    }
    private HungrySingleton(){
        if(hungrySingleton != null){
            throw new RuntimeException("单例构造器禁止反射调用");
        }
    }
    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }

    private Object readResolve(){
        return hungrySingleton;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return getInstance();
    }
}

LazyDoubleCheckSingleton

package com.geely.design.pattern.creational.singleton;

/**
 * Created by geely
 */
public class LazyDoubleCheckSingleton {
    private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null;
    private LazyDoubleCheckSingleton(){

    }
    public static LazyDoubleCheckSingleton getInstance(){
        if(lazyDoubleCheckSingleton == null){
            synchronized (LazyDoubleCheckSingleton.class){
                if(lazyDoubleCheckSingleton == null){
                    lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
                    //1.分配内存给这个对象
//                  //3.设置lazyDoubleCheckSingleton 指向刚分配的内存地址
                    //2.初始化对象
//                    intra-thread semantics
//                    ---------------//3.设置lazyDoubleCheckSingleton 指向刚分配的内存地址
                }
            }
        }
        return lazyDoubleCheckSingleton;
    }
}

LazySingleton

package com.geely.design.pattern.creational.singleton;

/**
 * Created by geely
 */
public class LazySingleton {
    private static LazySingleton lazySingleton = null;
    private LazySingleton(){
        if(lazySingleton != null){
            throw new RuntimeException("单例构造器禁止反射调用");
        }
    }
    public synchronized static LazySingleton getInstance(){
        if(lazySingleton == null){
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }

//    public static void main(String[] args) throws NoSuchMethodException, 
//  IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//        Class objectClass = LazySingleton.class;
//        Constructor c = objectClass.getDeclaredConstructor();
//        c.setAccessible(true);
//
//        LazySingleton o1 = LazySingleton.getInstance();
//
//        Field flag = o1.getClass().getDeclaredField("flag");
//        flag.setAccessible(true);
//        flag.set(o1,true);
//
//
//        LazySingleton o2 = (LazySingleton) c.newInstance();
//
//        System.out.println(o1);
//        System.out.println(o2);
//        System.out.println(o1==o2);
//    }

}

StaticInnerClassSingleton

package com.geely.design.pattern.creational.singleton;

/**
 * Created by geely
 */
public class StaticInnerClassSingleton {
    private static class InnerClass{
        private static StaticInnerClassSingleton staticInnerClassSingleton =
                           new StaticInnerClassSingleton();
    }
    public static StaticInnerClassSingleton getInstance(){
        return InnerClass.staticInnerClassSingleton;
    }
    private StaticInnerClassSingleton(){
        if(InnerClass.staticInnerClassSingleton != null){
            throw new RuntimeException("单例构造器禁止反射调用");
        }
    }


} 

破坏单例模式的三种方法以及对应的解决办法

package designPattern;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Test {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException,
     InvocationTargetException, InstantiationException, CloneNotSupportedException, 
     IOException, ClassNotFoundException {
        Class clazz = StaticInnerClassSignletonLazy.class;
        Constructor constructor =  clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        StaticInnerClassSignletonLazy newInstance = 
                                   (StaticInnerClassSignletonLazy)constructor.newInstance();
        StaticInnerClassSignletonLazy instance = StaticInnerClassSignletonLazy.getSignleton();
        System.out.println(instance == newInstance);

 

        StaticInnerClassSignletonLazy instance = StaticInnerClassSignletonLazy.getSignleton();
        StaticInnerClassSignletonLazy newInstance = (StaticInnerClassSignletonLazy)instance.clone();
        System.out.println(instance == newInstance);


        StaticInnerClassSignletonLazy instance = StaticInnerClassSignletonLazy.getSignleton();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("132"));
        oos.writeObject(instance);
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("132"));
        StaticInnerClassSignletonLazy newInstance = (StaticInnerClassSignletonLazy)ois.readObject();
        System.out.println(newInstance == instance);

    }


}

单例模式中用volatile和synchronized来满足双重检查锁机制

背景:我们在实现单例模式的时候往往会忽略掉多线程的情况,就是写的代码在单线程的情况下是没问题的,但是一碰到多个线程的时候,由于代码没写好,就会引发很多问题,而且这些问题都是很隐蔽和很难排查的。

例子1:没有volatile修饰的uniqueInstance

public class Singleton {
    private static Singleton uniqueInstance;

    private Singleton(){
    }

    public static Singleton getInstance(){
        if(uniqueInstance == null){ //#1
            synchronized(Singleton.class){ //#2
                if(uniqueInstance == null){ //#3
                    uniqueInstance = new Singleton(); //#4
                    System.out.println(Thread.currentThread().getName() + ": uniqueInstance is initalized..."); //#5.1
                } else {
                    System.out.println(Thread.currentThread().getName() + ": uniqueInstance is not null now..."); //#5.2
                }
            }
        }
        return uniqueInstance;
    }
}
 1 public class TestSingleton {
 2     public static void main(final String[] args) throws InterruptedException {
 3         for (int i = 1; i <= 100000; i++) {
 4             final Thread t1 = new Thread(new ThreadSingleton());
 5             t1.setName("thread" + i);
 6             t1.start();
 7         }
 8     }
 9 
10     public static class ThreadSingleton implements Runnable {
11         @Override
12         public void run() {
13             Singleton.getInstance();
14         }
15     }
16 }

这里面的结果有可能会是:(没有真正重现过,太难模拟了)

1 thread2: uniqueInstance is initalized...
2 thread3: uniqueInstance is initalized...
Singleton被实例化两次了,和我们的单例模式设计期望值不一致:类永远只被实例化一次.

原因分析:
1. thread2进入#1, 这时子线程的uniqueInstance都是为空的,thread2让出CPU资源给thread3
2. thread3进入#1, 这时子线程的uniqueInstance都是为空的, thread3让出CPO资源给thread2
3. thread2会依次执行#2,#3,#4, #5.1,最终在thread2里面实例化了uniqueInstance。thread2执行完毕让出CPO资源给thread3
4. thread3接着#1跑下去,跑到#3的时候,由于#1里面拿到的uniqueInstance还是空(并没有及时从thread2里面拿到最新的),所以thread3仍然会执行#4,#5.1
5. 最后在thread2和thread3都实例化了uniqueInstance

例子2:用volatile修饰的uniqueInstance

这里就不贴重复的代码了,因为只是加多一个volatile来修饰成员变量:uniqueInstance,

但是结果却是正确的了, 其中一个可能结果:

thread2: uniqueInstance is initalized
thread3: uniqueInstance is not null now...

原因分析:

volatile(java5):可以保证多线程下的可见性;

读volatile:每当子线程某一语句要用到volatile变量时,都会从主线程重新拷贝一份,这样就保证子线程的会跟主线程的一致。

写volatile: 每当子线程某一语句要写volatile变量时,都会在读完后同步到主线程去,这样就保证主线程的变量及时更新。

1. thread2进入#1, 这时子线程的uniqueInstance都是为空的(java内存模型会从主线程拷贝一份uniqueInstance=null到子线程thread2),thread2让出CPU资源给thread3 2. thread3进入#1, 这时子线程的uniqueInstance都是为空的(java内存模型会从主线程拷贝一份uniqueInstance=null到子线程thread2), thread3让出CPO资源给thread2 3. thread2会依次执行#2,#3,#4, #5.1,最终在thread2里面实例化了uniqueInstance(由于是volatile修饰的变量,会马上同步到主线程的变量去)。thread2执行完毕让出CPU资源给thread3 4. thread3接着#1跑下去,跑到#3的时候,会又一次从主线程拷贝一份uniqueInstance!=null回来,所以thread3就直接跑到了#5.2 5. 最后在thread3不再会重复实例化uniqueInstance了

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Mysqls

    是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。 事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资...

    大学里的混子
  • 二叉树的非递归遍历

    采用2个栈,这个与前序遍历类似,只不过是在该打印的时候,用一个栈将其存放起来,最后打印。

    大学里的混子
  • 圆圈中最后剩余的数字

    0,1,2,3 ,. . . ,n-1这n个数字排成一个圆圈,从数字0开始每次从这个圆圈里删除第m个数字,求这个圆圈里剩余的最后一个数字。

    大学里的混子
  • static示例

    爱学习的孙小白
  • Java面试必问通信框架NIO,原理详解

    新的输入/输出 (NIO) 库是在 JDK 1.4 中引入的,弥补了原来的 I/O 的不足,提供了高速的、面向块的 I/O。

    李红
  • Jwt服务间认证

    新进部门项目的小伙伴经常会问一些关于Jwt权限认证的问题,结合项目架构以及网络资料,分享一些关于我们系统对Jwt的使用情况

    3号攻城狮
  • 基于Netty和SpringBoot实现一个轻量级RPC框架-Server篇

    在前置的《基于Netty和SpringBoot实现一个轻量级RPC框架-协议篇》一文中已经定义了一个相对简单的RPC私有协议,并且实现了对应的编码和解码模块。这...

    Throwable
  • AsyncTask实现机制

    execute先调用onPreExecute()(可见,onPreExecute是自动调用的)然后调用exec.execute(mFuture)

    提莫队长
  • 如何实现一个可以用 await 异步等待的 Awaiter

    发布于 2017-10-29 08:38 更新于 2017-10...

    walterlv
  • .net core 源码解析-web app是如何启动并接收处理请求

    最近.net core 1.1也发布了,蹒跚学步的小孩又长高了一些,园子里大家也都非常积极的在学习,闲来无事,扒拔源码,涨涨见识。

    旺财的城堡

扫码关注云+社区

领取腾讯云代金券