单例模式的详解与实现(终极)

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));
	}
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值