Java多线程编程16 -单例模式与多线程

- 创建单例

1. 延迟加载/懒汉模式
public class Singleton {
	//本类内部创建对象实例
	private static Singleton instance = null;
    // 1.构造方法私有化,外部不能new
	private Singleton() {
 
	}
 
   //提供一个公有的静态方法,返回实例对象
	public static Singleton getInstance() {
		if (instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}

调用时序图:

单例模式的懒汉式体现了缓存的思想,延时加载就是一开始不要加载资源或者数据,一直等,等到马上就要使用这个资源的或者数据了,躲不过去了才去加载。

懒汉式是典型的时间换空间,但是不加同步的懒汉式是线程不安全的:
在这里插入图片描述
解决办法:

2. 给 getInstance() 方法加synchronized锁,但是这个方法效率比较低。
3. 双重检查加锁 (好像被抛弃了)
public class Singleton {
	private volatile static Singleton instance = null;
	// 私有化构造方法
	private Singleton() {
 
	}
 
	public static Singleton getInstance() {
		if (instance == null) { // 检查,只有第一次才进去,其他都不用等待锁
			synchronized (Singleton.class) {  // 加锁
				if (instance == null) { // 检查
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
}
4. 立即加载/饿汉模式
public class Singleton {
	private static Singleton instance = new Singleton(); // 直接加载
	// 私有化构造方法
	private Singleton() {
	}
	public static Singleton getInstance() { // 不用判断是不是NULL
		return instance;
	}
}

饿汉式是典型的空间换时间,当类装载的时候就会创建类实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要判断了,节省了运行时间。

5. 使用静态内部类实现单例模式
public class Singleton {

	private static class SingletonHoler { // 新建静态内部类
		//静态初始化器,由JVM来保证线程安全
		private static Singleton instance = new Singleton();
	}
	
	private Singleton() {
	}
	public static Singleton getInstance() {
		return SingletonHoler.instance;
	}
}

当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。

6. 枚举方式
/**
 * 枚举模式
 */
public class EnumSingleton {

    private EnumSingleton(){}
    /**
     * 采用 内部类 枚举的方式 实例化对象
     */
    private enum EnumSingletonDemo{
        INSTANCE;// enum实例序列的最后要添加一个分号
        private  EnumSingleton instance; // 定义指向上面的INSTANCE,构造方法私有
        EnumSingletonDemo(){
            instance=new EnumSingleton();
        }
    }

    public static EnumSingleton getInstance(){
        return EnumSingletonDemo.INSTANCE.instance;
    }
    /**
     * 测试
     * @param args
     */
    public static void main(String[] args) {
        for(int i=0;i<20;i++){
            new Thread(()->{
                System.out.println(EnumSingleton.getInstance());
            }).start();
        }
    }
}

枚举有点类似与静态代码块,使用枚举类的时候,构造方法会被自动调用。

破坏单例模式的三种方式
  1. 反射
  2. 序列化
  3. 克隆

解决方案如下:

1、防止反射

定义一个全局变量,当第二次创建的时候抛出异常

2、防止克隆破坏

重写clone(),直接返回单例对象

3、防止序列化破坏

添加readResolve(),返回Object对象


public class SingletonTest {
	public static void main(String[] args) throws Exception{

		 System.out.println("-----------序列化----------------------");
	     Singleton originSingleton = Singleton.getInstance();
	     ByteArrayOutputStream bos = new ByteArrayOutputStream();
	     ObjectOutputStream oos = new ObjectOutputStream(bos);
	     oos.writeObject(Singleton.getInstance());
	     ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
	     ObjectInputStream ois = new ObjectInputStream(bis);
	     Singleton serializeSingleton = (Singleton) ois.readObject();
	     System.out.println(originSingleton == serializeSingleton);//false
	      
	        System.out.println("-----------反射----------------------");
	        //通过反射获取
	        Constructor<Singleton> cons = Singleton.class.getDeclaredConstructor();
	        cons.setAccessible(true);
	        Singleton reflextSingleton = cons.newInstance();
	        System.out.println(reflextSingleton == originSingleton);//false
	        
	        System.out.println("---------------------------克隆----------------------");
	        
	        Singleton cloneSingleton = (Singleton) originSingleton.clone();
	        System.out.println(cloneSingleton == originSingleton);//false
	    
	}
	
	private static class Singleton  implements Serializable,Cloneable{
		
		private static volatile boolean isCreate = false;//默认是第一次创建
		/**
		 * 1.构造方法私有化,外部不能new
		 */
		private Singleton() {
			if(isCreate) {
				throw new RuntimeException("已然被实例化一次,不能在实例化");
			}
			isCreate = true;
		}
		
		
		//2.本类内部创建对象实例
		private static  volatile  Singleton instance;
		
		//3.提供一个公有的静态方法,返回实例对象
		public static  Singleton getInstance() {
			if(instance == null) {
				synchronized (Singleton.class) {
					if(instance == null) {
						instance = new Singleton();
					}
				}
			}
			return instance;
		}
		
		@Override
		 protected Object clone() throws CloneNotSupportedException {
		     return instance;
		 }
		
		/**
		 * 防止序列化破环,在从文件读入的时候使用此方法
		 */
		private Object readResolve() {
	        return instance;
	    }
	}
}

补充: hashCode():
用 实例化的对象的hashCode判断是不是一个实例:
myObject.hashCode() --> 输出这个对象的唯一hashCode.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值