单例模式的多种玩法
Posted 怒放de每一天
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式的多种玩法相关的知识,希望对你有一定的参考价值。
饿汉单例模式
简单饿汉单例模式
public class HungrySingleton {
private static final HungrySingleton hungrySington = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance(){
return hungrySington;
}
}
静态代码块实现饿汉模式
public class HungryStaticSingleton {
private static final HungryStaticSingleton hungrySington;
static {
hungrySington = new HungryStaticSingleton();
}
private HungryStaticSingleton() {
}
public static HungryStaticSingleton getInstance() {
return hungrySington;
}
}
懒汉单例模式
简单懒汉单例模式(线程不安全)
public class LazySimplySington {
private static LazySimplySington lazy;
private LazySimplySington() {
}
//jdk1.8之后对synchronized性能优化不少
//不可避免还是存在一定的性能问题
public synchronized static LazySimplySington getInstance() {
if (lazy == null) {
lazy = new LazySimplySington();
}
return lazy;
}
}
双重检查实现单例模式(线程安全)
注意指令重排序问题,需要单独加voliate关键字
public class LazyDoubleCheckSington {
private volatile static LazyDoubleCheckSington lazy = null;
private LazyDoubleCheckSington() {
}
//jdk1.8之后对synchronized性能优化不少
//不可避免还是存在一定的性能问题
public static LazyDoubleCheckSington getInstance() {
if (lazy == null) {
synchronized (LazyDoubleCheckSington.class) {
if (lazy == null) {
lazy = new LazyDoubleCheckSington();
//指令重排序的问题:也就是 第二步和第三步会颠倒,
// 解决方式 变量上加voliate,让线程可见
//CPU执行时候会转换成JVM指令执行
//1.分配内存给这个对象
//2.初始化对象
//4.用户初次始化
}
}
}
return lazy;
}
}
内部类实现单例模式(线程安全)
public class LazyInnerClassSington {
//虽然构造方法有了,但是逃不过反射的法眼
private LazyInnerClassSington() {
}
//懒汉式单例
//LazyHolder里面的逻辑需要等到外部方法调用时才执行
//巧妙利用了内部类的特性
//JVM底层执行逻辑,完美的避免了线程安全问题
public static final LazyInnerClassSington getInstance(){
return LazyHolder.lazy;
}
private static class LazyHolder{
private static final LazyInnerClassSington lazy = new LazyInnerClassSington();
}
}
暴力破解单例
反射暴力破解
public class LazyInnerClassSingtonTest {
public static void main(String[] args) {
try {
//反射,破坏了单例
Class<?> clazz = LazyInnerClassSington.class;
Constructor<?> c = clazz.getDeclaredConstructor(null);
c.setAccessible(true);
Object o = c.newInstance();
Object o2 = c.newInstance();
System.out.println(o == o2);
}catch (Exception e){
e.printStackTrace();
}
}
}
解决方式:
在构造函数内部添加判断
//虽然构造方法有了,但是逃不过反射的法眼
private LazyInnerClassSington() {
if (LazyHolder.lazy != null){
throw new RuntimeException("不允许构建多个实例");
}
}
测试:完整代码:
public class LazyInnerClassSington {
//虽然构造方法有了,但是逃不过反射的法眼
private LazyInnerClassSington() {
if (LazyHolder.lazy != null){
throw new RuntimeException("不允许构建多个实例");
}
}
//懒汉式单例
//LazyHolder里面的逻辑需要等到外部方法调用时才执行
//巧妙利用了内部类的特性
//JVM底层执行逻辑,完美的避免了线程安全问题
public static final LazyInnerClassSington getInstance(){
return LazyHolder.lazy;
}
private static class LazyHolder{
private static final LazyInnerClassSington lazy = new LazyInnerClassSington();
}
}
序列化和反序列化暴力破解
来个饿汉单例
//反序列化时导致单例破坏
public class SeriableSingleton implements Serializable {
//序列化就是说把内存中的状态通过转换成字节码的形式
//从而转换一个IO流,写入到其他地方(可以是磁盘、网络IO)
//内存中状态给永久保存下来了
//反序列化
//讲已经持久化的字节码内容,转换为IO流
//通过IO流的读取,进而将读取的内容转换为Java对象
//在转换过程中会重新创建对象new
public final static SeriableSingleton INSTANCE = new SeriableSingleton();
private SeriableSingleton(){}
public static SeriableSingleton getInstance(){
return INSTANCE;
}
破解
public class SeriableSingletonTest {
public static void main(String[] args) {
SeriableSingleton s1 = null;
SeriableSingleton s2 = SeriableSingleton.getInstance();
FileOutputStream fos = null;
try {
fos = new FileOutputStream("SeriableSingleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("SeriableSingleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
s1 = (SeriableSingleton)ois.readObject();
ois.close();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1 == s2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
解决方式resolve方法
加个方法:
//序列化解决单例问题的方式
//重写readResolve方法,只不过是覆盖了反序列化出来的对象
//还是创建了两次,发生在JVM层面,相对来说比较安全
//之前反序列化出来的对象会被GC回收
private Object readResolve(){
return INSTANCE;
}
完整代码:
public class SeriableSingletonTest {
public static void main(String[] args) {
SeriableSingleton s1 = null;
SeriableSingleton s2 = SeriableSingleton.getInstance();
FileOutputStream fos = null;
try {
fos = new FileOutputStream("SeriableSingleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("SeriableSingleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
s1 = (SeriableSingleton)ois.readObject();
ois.close();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1 == s2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
注册类单例
枚举实现单例(推荐)
可以同时实现防止反射和序列化反序列化暴力破解
public enum EnumSingleton {
INSTANCE;
private String name;
public String getData() {
return name;
}
public void setData(String data) {
this.name = data;
}
public static EnumSingleton getInstance(){
return INSTANCE;
}
}
使用jad工具查看枚举类的反编译代码
反编译代码:
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: EnumSingleton.java
package com.example.sington.register;
public final class EnumSingleton extends Enum
{
public static EnumSingleton[] values()
{
return (EnumSingleton[])$VALUES.clone();
}
public static EnumSingleton valueOf(String name)
{
return (EnumSingleton)Enum.valueOf(com/example/sington/register/EnumSingleton, name);
}
private EnumSingleton(String s, int i)
{
super(s, i);
}
public Object getData()
{
return data;
}
public void setData(Object data)
{
this.data = data;
}
public static EnumSingleton getInstance()
{
return INSTANCE;
}
public static final EnumSingleton INSTANCE;
private Object data;
private static final EnumSingleton $VALUES[];
static
{
INSTANCE = new EnumSingleton("INSTANCE", 0);
$VALUES = (new EnumSingleton[] {
INSTANCE
});
}
}
容器实现单例
public class ContainerSingleton {
private ContainerSingleton() {
}
private static Map<String, Object> ioc = new ConcurrentHashMap<>();
public static Object getBean(String className) {
synchronized (ioc){
if (!ioc.containsKey(className)) {
Object obj = null;
try {
obj = Class.forName(className).getInterfaces();
ioc.put(className, obj);
} catch (Exception e) {
e.printStackTrace();
}
}
return ioc.get(className);
}
}
}
public class Pojo {
}
public class ContainerSingletonTest {
public static void main(String[] args) {
try {
long start = System.currentTimeMillis();
ConcurrentExecutor.execute(new ConcurrentExecutor.RunHandler() {
public void handler() {
Object obj = ContainerSingleton.getBean("com.example.Pojo");
System.out.println(System.currentTimeMillis() + ": " + obj);
}
}, 10,6);
long end = System.currentTimeMillis();
System.out.println("总耗时:" + (end - start) + " ms.");
}catch (Exception e){
e.printStackTrace();
}
}
}
线程间实现单例ThreadLocal
public class ThreadLocalSingleton {
private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =
new ThreadLocal<ThreadLocalSingleton>() {
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
private ThreadLocalSingleton() {
}
public static ThreadLocalSingleton getInstance() {
return threadLocalInstance.get();
}
}
public class ExectorThread implements Runnable {
@Override
public void run() {
// LazySimplySington instance = LazySimplySington.getInstance();
// LazyDoubleCheckSington instance = LazyDoubleCheckSington.getInstance();
ThreadLocalSingleton instance = ThreadLocalSingleton.getInstance();
System.out.println(Thread.currentThread().getName()+" ---"+instance);
ThreadLocalSingleton instance2 = ThreadLocalSingleton.getInstance();
System.out.println(Thread.currentThread().getName()+" ---"+instance2);
ThreadLocalSingleton instance3 = ThreadLocalSingleton.getInstance();
System.out.println(Thread.currentThread().getName()+" ---"+instance3);
ThreadLocalSingleton instance4 = ThreadLocalSingleton.getInstance();
System.out.println(Thread.currentThread().getName()+" ---"+instance4);
ThreadLocalSingleton instance5 = ThreadLocalSingleton.getInstance();
System.out.println(Thread.currentThread().getName()+" ---"+instance5);
System.out.println(Thread.currentThread().getName()+" ---"+instance);
}
}
public class ThreadLocalSingletonTest {
public static void main(String[] args) {
System.out.println(ThreadLocalSingleton.getInstance());
System.out.println(ThreadLocalSingleton.getInstance());
System.out.println(ThreadLocalSingleton.getInstance());
System.out.println(ThreadLocalSingleton.getInstance());
System.out.println(ThreadLocalSingleton.getInstance());
Thread t1 = new Thread(new ExectorThread());
Thread t2 = new Thread(new ExectorThread());
t1.start();
t2.start();
System.out.println("End");
}
}
可以看到线程间是单例的。
单例模式的本质
本质:控制实例数目。(研磨设计模式)
。
笔记和代码地址
代码:https://github.com/hufanglei/pattern-learn/tree/master/src/main/java/com/example/sington
笔记https://blog.csdn.net/baidu_21349635/article/details/106067581
微信订阅号:搜索:怒放de每一天
个人微信公号,搜索:i69032866
不定时推送相关文章,期待和大家一起成长!!
完
以上是关于单例模式的多种玩法的主要内容,如果未能解决你的问题,请参考以下文章