5中单例模式且防止反射和反序列化破解(不针对枚举)并测试效率

单例模式

单例模式,顾名思义就是只有一个实例,并且她自己负责创建自己的对象,这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象
核心代码:1构造方法私有化, 2. private static 3.提供一个公共方法

1.饿汉式

饿汉式,从名字上也很好理解,就是“比较勤”,实例在初始化的时候就已经建好了,不管你有没有用到,都先建好了再说。好处是没有线程安全的问题,调用效率高.坏处是浪费内存空间 无法延时加载。

public class SingletonDemo2 {
	// 类初始化时 立即加载这个对象 。 加载类时是一个天然安全的模式
	private static SingletonDemo2 instance = new SingletonDemo2();

	private SingletonDemo2() {
	}

	// 方法没有同步 调用效率高
	public static SingletonDemo2 getInstance() {
		return instance;
	}
}

2.懒汉式

懒汉式,顾名思义就是实例在用到的时候才去创建,“比较懒”,用的时候才去检查有没有实例,如果有则返回,没有则新建。有线程安全和线程不安全两种写法,区别就是synchronized关键字。
优点:线程安全 延时加载 资源利用率高 缺点: 并发效率低

public class SingletonDemo1 {
	// 类初始化不初始化这个对象 。延时加载  真正用的时候加载
	private static SingletonDemo1 instance;

	private SingletonDemo1() {//私有化构造器
	}

	// 方法同步 调用效率低
	public static synchronized SingletonDemo1 getInstance() {
		if(null==instance) {
			instance = new SingletonDemo1();
		}
		return instance;
	}
}

3.双重检测锁

双检锁,又叫双重校验锁,综合了懒汉式和饿汉式两者的优缺点整合而成。看上面代码实现中,特点是在synchronized关键字内外都加了一层 if 条件判断,这样既保证了线程安全,又比直接上锁提高了执行效率,还节省了内存空间。
问题:由于编译器优化原因和jvm底层内部模型原因 偶尔会出现问题
解决方法:对之前代码加入volatile关键字,即可实现线程安全的单例模式。

public class SingletonDemo3 {
	// 类初始化时 立即加载这个对象 。 加载类时是一个天然安全的模式
	private static volatile SingletonDemo3 instance = null;

	private SingletonDemo3() {
	}

	// 方法没有同步 调用效率高
	public static SingletonDemo3 getInstance() {
		if (instance == null) {
			synchronized (SingletonDemo3.class) {
				if (instance == null) {
					instance = new SingletonDemo3();
				}
			}
		}

		return instance;
	}
}

4.静态内部类

静态内部类的方式效果类似双检锁,但实现更简单。但这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
这种方式线程安全 调用效率高 实现了 延时加载

public class SingletonDemo4 {
	private static class SingletonClassInstance {
		private static final SingletonDemo4 instance = new SingletonDemo4();
	}
	private SingletonDemo4() {
	}

	public static SingletonDemo4 getInstance() {
		
		return SingletonClassInstance.instance;
	}
}

5.枚举

枚举的方式是比较少见的一种实现方式,并且她还自动支持序列化机制,绝对防止多次实例化。
枚举实现刊例模式 没有延时加载

  • 通过Singleton.INSTANCE进行操作
public enum SingletonDemo5 {
	//这个枚举元素  本身就是单例
	INSTANCE;
	//添加自己需要的操作
	public void singletonOperation() {
		
	}
	 
}

好了,上面就是单例模式的五种主要写法。我们来总结下,一般情况下,懒汉式(包含线程安全和线程不安全梁总方式)都比较少用;饿汉式和双检锁都可以使用,可根据具体情况自主选择;在要明确实现 lazy loading 效果时,可以考虑静态内部类的实现方式;若涉及到反序列化创建对象时,大家也可以尝试使用枚举方式。

6.防止反射破解

Class<SingletonDemo6> clazz = (Class<SingletonDemo6>) Class.forName("com.sxt.singleton.SingletonDemo6");
		//获得无参构造器
		Constructor<SingletonDemo6> c = clazz.getDeclaredConstructor(null);
		c.setAccessible(true);	//接触安全模式
		SingletonDemo6 s3 = c.newInstance();
		SingletonDemo6 s4 = c.newInstance();
		
		System.out.println(s3);
		System.out.println(s4);

解决办法

	// 类初始化不初始化这个对象 。延时加载  真正用的时候加载
	private static SingletonDemo6 instance;

	private SingletonDemo6() {//私有化构造器
		if(instance!=null) {//防止反射破解
			throw new RuntimeException();
		}
	}

	// 方法同步 调用效率低
	public static synchronized SingletonDemo6 getInstance() {
		if(null==instance) {
			instance = new SingletonDemo6();
		}
		return instance;
	}

如果存在一个对象 报异常

7.防止反序列化破解

//通过反序列化的方式构造多个对象
		FileOutputStream fos = new FileOutputStream("d:/a.txt");
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		oos.writeObject(s1);
		oos.close();
		fos.close();
		
		ObjectInputStream ois = new  ObjectInputStream(new FileInputStream("d:/a.txt"));
		SingletonDemo6 s3 =  (SingletonDemo6) ois.readObject();
		System.out.println(s3);

解决办法

//反序列化时 如果有一个readResolve方法则直接返回此方法指定的对象  而不需要单独在创建对象
	private Object readResolve() throws Exception{
		return instance;
	}

8测试效率

public class Client3 {

	public static void main(String[] args) throws Exception {
		
		long start  =System.currentTimeMillis();
		
		int threadNum=10;
		final CountDownLatch countDownLatch = new CountDownLatch(threadNum);
		
		for (int i = 0; i < 10; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					for (int i = 0; i < 1000000; i++) {
						Object o = SingletonDemo1.getInstance();
					}
					countDownLatch.countDown();
				}
			}).start();
		}
		countDownLatch.await();//main线程阻塞 知道计数器变为0  才会继续执行
		
		long end  =System.currentTimeMillis();
		System.out.println("总耗时:"+(end-start));
	}
}

借助CountDownLatch类 countDownLatch.countDown(); 计数器作用countDownLatch.await();//main线程阻塞 直到计数器变为0 才会继续执行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值