1、主要介绍懒汉式,饿汉式,静态内部类,双重加锁,枚举五种单例模式的实现过程
2、针对于单例模式的破解与防护的实现
3、比较其5中方式在多线程环境中的效率
package com.behavior_model.danli;
/**
* 单例模式
* @author pshdhx
* 1、保证一个类只有一个实例;
* 2、并且提供一个该实例的全局访问点
*
* 应用场景:
* 1、windows的任务管理器就是一个典型的单例模式;
* 2、windows的回收站也是个典型的单例应用;在整个运行过程中,回收站一直维护这仅有的一个实例
* 3、网站的计数器,一般也采用单例模式,否则难以同步;
* 4、数据库的连接池的设计一般也采用单例模式,因为数据库连接是一种数据资源;、
* 5、【读取配置文件的类,一般只有一个对象,没有必要每次使用配置文件的数据,每次new一个对象去读取】
* 6、Application也是单例的典型应用;Servlet编程中会涉及到
* 7、在Spring中,每个Bean模式是单例的,这样做的优点是Spring容器可以管理
* 8、在Servlet编程中,每个Servlet也是单例
* 9、在Spring MVC中,控制对象也是单例
*
* 优点:只生成一个实例,减少了系统性能的开销;当一个对象的产生需要比较多的资源时,【读取配置,产生其他依赖对象】,则
* 可以通过应用启动时产生一个单例对象,然后永久驻留内存的方式来解决;
* 例如可以设计一个单例类:负责处理数据表的映射
*
* 常见的五种单例模式的实现方式
* 1、饿汉式(线程安全,调用效率高。但是,不能延时加载)【不能延时】
* 2、懒汉式(线程安全,调用效率不高,但是可以延时加载)【能延时,我们所期望的】
*
* 3、双重检测锁式(由于JVM底层内部模型的原因,偶尔会出现问题,不建议使用)
* 4、静态内容类式(线程安全,调用效率高,但可以延时加载)【能延时】
* 5、枚举单例(线程安全,调用效率高,不能延时加载)【不能延时】
*
*/
public class DanLi {
public static void main(String[] args) {
EHan e1 = EHan.getInstance();
EHan e2 = EHan.getInstance();
System.out.println(e1==e2); //true
}
}
/**
* 饿汉式-单例模式
*
* static变量会在类装载时实例化,此时不会涉及多个线程访问该对象的问题
* 虚拟机只保证只会装载一次该类,肯定不会发生并发访问的问题。因此,synchronized关键字可以省略
*
* 问题:如果只是加载本类,而不是调用getInstance(),真是永远没有调用,则会造成资源浪费。
*
* @author pshdhx
*
*/
class EHan{
private static final EHan eHan = new EHan(); //[加载类的时候,是一个天然的线程安全的模式] 没有延时加载的优势
private EHan(){}//私有构造器
public static /*synchronized*/ EHan getInstance(){ //不需要同步的话,调用效率就高
return eHan;
}
}
//总结:两私一公
/**
* 懒汉式
* @author pshdhx
* 要点:延迟加载,真正用的的使用才加载
* 问题:资源利用率高了。但是,每次调用getInstance()方法都要同步,并发效率低
*/
class LanHanShi{
private static LanHanShi lanHanshi;
private LanHanShi(){};
public static synchronized LanHanShi getInstance(){ //synchronized避免在并发量高的时候创建多个对象
if(lanHanshi==null){
lanHanshi = new LanHanShi();
}
return lanHanshi;
}
}
//总结:两私一公,不实例化,要同步化
/**
* 双重检测锁模式--可能会出问题
* @author pshdhx
* 同步块放到了方法内部的if里边,而不是整个方法的同步,提高了执行的效率;
* 不必每次获取对象时都进行同步,只有第一次才同步【才进行双重检测】,创建了以后就没有必要了
*
*问题:编译器优化原因和jvm底层内部模型原因可能会出错;
*/
class ShuangChongJianCeSuo{
private static ShuangChongJianCeSuo instance = null;
private ShuangChongJianCeSuo(){}
public static ShuangChongJianCeSuo getInstance(){
if(instance==null){
ShuangChongJianCeSuo ins;
synchronized (ShuangChongJianCeSuo.class) {
ins = instance;
if(ins==null){
synchronized (ShuangChongJianCeSuo.class) {
if(ins==null){
ins = new ShuangChongJianCeSuo();
}
}
instance=ins;
}
}
}
return instance;
}
}
//总结:两私一公,不实例化,第一次进行双重检测,三个if;
/**
* 静态内部类实现
* @author pshdhx
*
*/
class JingTaiNeiBuLei{
private static class JingTaiNeiBuLeiInstance{
private static final JingTaiNeiBuLei instalce = new JingTaiNeiBuLei();
}
private JingTaiNeiBuLei(){}
public static JingTaiNeiBuLei getInstance(){
return JingTaiNeiBuLeiInstance.instalce;
}
}
//总结:两私一公 :使用了静态内部类
/**
*
* @author pshdhx
* 枚举类型:使用了enum,底层是jdk的封装,因此线程安全,效率高,但是不能延迟加载
*/
enum MeiJuLeiXing{
INSTANCE;
public void operation(){
System.out.println("我是枚举");
}
}
单例模式除了枚举类型都是可以通过反射和反序列化来破解的,但是也有针对于破解的方法
package com.behavior_model.danli;
import java.io.ObjectStreamException;
import java.io.Serializable;
/**
*
* @author ASUS
*
*/
public class SingletionDemo02 implements Serializable{
//1 创建静态私有属性
private static SingletionDemo02 instance;
//2 创建私有构造器 避免外部直接创建对象
// private SingletionDemo02(){}
//防止反射破解单例模式
private SingletionDemo02(){
if (null != instance){
throw new RuntimeException();
}
}
//3 对象提供访问的公共静态方法 访问该属性
public static synchronized SingletionDemo02 getInstance() throws InterruptedException {
if (null == instance){
instance=new SingletionDemo02();
}
return instance;
}
//方法作用 : 在反序列化的时候 直接调用这个方法返回当前的对象, 而不需要在创建对象
private Object readResolve() throws ObjectStreamException {
return instance;
}
}
package com.behavior_model.danli;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
public class Test {
public static void main (String[] args) {
try {
SingletionDemo02 instance1 = SingletionDemo02.getInstance();
SingletionDemo02 instance2 = SingletionDemo02.getInstance();
System.out.println(instance1);
System.out.println(instance2);
System.out.println("________通过反射方式破解单例模式________");
Class<?> aClass = Class.forName("com.behavior_model.danli.SingletionDemo02");
//获取默认构造器
Constructor<?> constructor = aClass.getDeclaredConstructor(null);
//跳过安全检查
// constructor.setAccessible(true);
// SingletionDemo02 singletionDemo02 = (SingletionDemo02) constructor.newInstance();
// SingletionDemo02 singletionDemo03 = (SingletionDemo02) constructor.newInstance();
// System.out.println(singletionDemo02);
// System.out.println(singletionDemo03);
//通过反序列化来破解单例模式
FileOutputStream fos = new FileOutputStream("d:/a.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(instance1);
oos.close();
fos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt"));
SingletionDemo02 s = (SingletionDemo02) ois.readObject();
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.behavior_model.danli;
import java.util.concurrent.CountDownLatch;
/**
* 比较5中单例模式的效率 [多线程环境下]
* @author pshdhx
*
*/
public class Client {
public static void main(String[] args) throws InterruptedException {
long s = System.currentTimeMillis();
int count=10;
final CountDownLatch countDownLatch = new CountDownLatch(count); //相当于10个线程计数器,减为0的时候,往下执行
for(int i=0;i<count;i++){
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
for(int j=0;j<10000;j++){
EHan instance = EHan.getInstance();
}
countDownLatch.countDown();
}
}).start();
}
countDownLatch.await();// while 循环等待 main线程执行,其他线程并行执行,知道其他线程执行完成后,main线程才往下执行
long e = System.currentTimeMillis();
System.out.println("总耗时:"+(e-s));
}
}