彻底玩转单例模式
资源来源:哔哩哔哩 - 狂神说
懒汉式、DCL饿汉式,深究!
饿汉式单例:
// 饿汉式单例
public class Hungry {
// 可能会浪费空间
private byte[] data1 = new byte[1024*1024];
private byte[] data2 = new byte[1024*1024];
private byte[] data3 = new byte[1024*1024];
private byte[] data4 = new byte[1024*1024];
// 私有构造器
private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
缺点: 可能会浪费空间
懒汉式单例:
public class LazyMan {
private LazyMan(){
System.out.println(Thread.currentThread().getName() + "ok");
}
private static LazyMan lazyMan;
public static LazyMan getInstance(){
if(lazyMan == null){
lazyMan = new LazyMan();
}
return lazyMan;
}
// 单线程下没问题。多线程并发测试
public static void main(String[] args){
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
测试结果:
修改:添加双重检测锁模式(DCL懒汉式)
public class LazyMan {
private LazyMan(){
System.out.println(Thread.currentThread().getName() + "ok");
}
// volatile 避免指令重排
private volatile static LazyMan lazyMan;
// 双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan == null){
synchronized(LazyMan.class){
if(lazyMan == null){
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
// 单线程下没问题。多线程并发测试
public static void main(String[] args){
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
测试结果:
静态内部类方式
// 静态内部类
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
反射与懒汉单例
破坏:通过 反射 破坏双重检测锁模式
// 反射!
public static void main(String[] args) throws Exception{
LazyMan instance1 = LazyMan.getInstance();
// 获取构造器
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
// 开启赋权(暴力反射)
declaredConstructor.setAccessible(true);
// 获取实例对象
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
测试结果:破坏了单例
防止:在无参构造里面加锁判断
public class LazyMan {
private LazyMan(){
synchronized (LazyMan.class){
if (lazyMan != null){
throw new RuntimeException("不要试图使用反射破坏单例!!!");
}
}
}
// volatile 避免指令重排
private volatile static LazyMan lazyMan;
// 双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan == null){
synchronized(LazyMan.class){
if(lazyMan == null){
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
}
测试结果:
破坏:第一次不new对象,使用反射获取两次实例对象。
public static void main(String[] args) throws Exception{
// LazyMan instance1 = LazyMan.getInstance();
// 获取构造器
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
// 开启赋权(暴力反射)
declaredConstructor.setAccessible(true);
// 同一个构造器
// 获取实例对象
LazyMan instance1 = declaredConstructor.newInstance();
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
防止:设置红绿灯标志位来防止
不通过反编译的技术,没法获取标志位!
public class LazyMan {
private static boolean flag = false;
private LazyMan(){
synchronized (LazyMan.class){
if(!flag){
flag = true;
}else {
throw new RuntimeException("不要试图使用反射破坏单例!!!");
}
}
}
}
破坏:通过反编译获取标志位,开启暴力反射进行赋值操作
// 反射!
public static void main(String[] args) throws Exception{
// LazyMan instance1 = LazyMan.getInstance();
// 获取属性字段
Field flag = LazyMan.class.getDeclaredField("flag");
// 暴力反射,获取私有属性
flag.setAccessible(true);
// 获取构造器
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
// 开启赋权(暴力反射)
declaredConstructor.setAccessible(true);
// 获取实例对象
LazyMan instance1 = declaredConstructor.newInstance();
// 修改标志位为false
flag.set(instance1,false);
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
防止:反射不能破坏枚举,写枚举类进行测试
package com.yanchi;
import java.lang.reflect.Constructor;
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class MyTest{
public static void main(String[] args) throws Exception{
EnumSingle instance1 = EnumSingle.INSTANCE;
// java.lang.NoSuchMethodException: com.yanchi.EnumSingle.<init>()
// 这个报错信息和预期不符合,通过反编译查看真实源代码
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
报错信息和预期不符合,通过反编译查看真实源代码
使用专业工具进行反编译:JAD
命令:jad -sjava 类文件
反编译得到的java文件:
// 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: EnumSingle.java
package com.yanchi;
public final class EnumSingle extends Enum
{
public static EnumSingle[] values()
{
return (EnumSingle[])$VALUES.clone();
}
public static EnumSingle valueOf(String name)
{
return (EnumSingle)Enum.valueOf(com/yanchi/EnumSingle, name);
}
private EnumSingle(String s, int i)
{
super(s, i);
}
public EnumSingle getInstance()
{
return INSTANCE;
}
public static final EnumSingle INSTANCE;
private static final EnumSingle $VALUES[];
static
{
INSTANCE = new EnumSingle("INSTANCE", 0);
$VALUES = (new EnumSingle[] {
INSTANCE
});
}
}
由此可知:枚举类中没有无参构造。
修改测试类继续测试:
package com.yanchi;
import java.lang.reflect.Constructor;
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class MyTest{
public static void main(String[] args) throws Exception{
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
// 报错信息:Cannot reflectively create enum objects
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
得到想要的结果:
测试结束!!!