单例懒汉式的多线程操作的安全问题

时间:2022-09-06 21:12:52

先看看什么是单例设计模式呢?

单例设计模式,也称单态(原子)设计模式。最初起始于建筑行业,建筑行业把建筑中中的一些常见问题进行了归纳总结,并给出了具体的解决方案。(简单理解就是解决问题的模板)

单例设计模式 ,是用来解决:程序中的所创建出来的对象只有一个(创建的对象在堆内存中只开辟一个空间)。

问题1只要使用new关键字,就可以创建不同的对象(如何保证只能有一个对象呢?)反向思考:不让其它程序去创建对象,就不会有不同对象存在于堆空间下

问题2:不让使用new创建对象了,那么唯一的一个对象怎么创建呢?可以在本类中创建一个属于本类自己的对象

问题3:在本类创建属于自己的对象,那么其它程序怎么去访问这个唯一对象? 可以对外提供一个public方法,把创建出来的唯一对象返回出去,供其它程序使用

根据分析的问题,怎么去实现呢?考虑一下问题

问题1:不让其它程序去使用new创建对象? 方法:把构造函数私有化

问题2:其它程序无法创建对象了,唯一的对象怎么出现?方法:在本类中创建一个属于本类自己的对象(自已创建自己的对象)

问题3:其它程序怎么去访问这个创建出来的唯一对象?方法:在本类中提供一个public方法,把创建出来的唯一对象,返回出去

基于以上三点,使用代码体现:

package com.Thread.demo;


//单例设计模式,懒汉式
class Single{
	
	//第一步:私有化构造函数
	private  Single(){}
	
	//第二步:创建本类对象,初始化为null
	private static  Single instance =null;
	
	//第三步:对外一个公共方法
	public static  Single getInstance(){
		
		//判断对象是否为空,就创建对象
		if (instance==null) {
			instance=new Single();
		}
		//把唯一的对象返回出去
		return instance;
		
	}

}
public class SingleDemo1 {

	public static void main(String[] args) {

		//获取单例对象
		Single s1 = Single.getInstance();
	}

}
当针对单例的懒汉式添加多线程后,该对象就不唯一了如:

package com.Thread.demo;

import sun.security.jca.GetInstance;

/**
 * 单例懒汉式的多线程操作的安全问题
 *
 */
class Single{
	
	//第一步:私有化构造函数
	private Single(){	}
		
	//第二步:创建本类自己对象,初始化为空
	public static Single instance =null;
	
	//对外提供一个公共方法,返回唯一对象
	public static Single getInstance(){
		//判断,为空时创建本类对象
		if (instance==null) {
			instance =new Single();
		}
		//返回唯一的对象
		return instance;
	}
}

//创建线程任务类,测试多线程操作单例的懒汉式的安全问题
class Task implements Runnable{
	//复写run方法,书写线程任务
	public void run() {
	
		//获取单例对象,,输出其地址
		System.err.println(Single.getInstance());
	}
	
}

public class SingleThreadDemo {

	public static void main(String[] args) {
		
		//创建任务对象
		Task task= new Task();
		
		//创建线程对象,将任务对象作为参数传递
		Thread  t1= new Thread(task);
		Thread  t2= new Thread(task);
		
		//开启线程
		t1.start();
		t2.start();
		
		
	}

}
运行结果
单例懒汉式的多线程操作的安全问题
        可以看出当开启两个线程去执行单例任务的时候,获取的两个对象地址不唯一!那么怎么解决呢?

方法很简单:就是对多线程共同操作的代码块添加一个锁,即保证线程任务同步!     

   
package com.Thread.demo;

import sun.security.jca.GetInstance;

/**
 * 单例懒汉式的多线程操作的安全问题
 *
 */
class Single{
	
	//第一步:私有化构造函数
	private Single(){	}
		
	//创建锁对象
	private static Object lock = new Object();
	
	//第二步:创建本类自己对象,初始化为空
	public static Single instance =null;
	
	//对外提供一个公共方法,返回唯一对象
	public static Single getInstance(){
		
		<span style="color:#ff0000;">//加判断是提高后续线程的执行效率
		if(instance==null){
		//加同步的目的是保证对象的唯一
		synchronized (lock) {
		if (instance==null) {
			instance =new Single();
		}
		}
		}</span>
		//返回唯一的对象
		return instance;
	}
}

//创建线程任务类,测试多线程操作单例的懒汉式的安全问题
class Task implements Runnable{
	//复写run方法,书写线程任务
	public void run() {
	
		//获取单例对象,,输出其地址
		System.out.println(Single.getInstance());
	}
}
public class SingleThreadDemo {

	public static void main(String[] args) {
		
		//创建任务对象
		Task task= new Task();
		
		//创建线程对象,将任务对象作为参数传递
		Thread  t1= new Thread(task);
		Thread  t2= new Thread(task);
		
		//开启线程
		t1.start();
		t2.start();
		
		
	}

}
单例懒汉式的多线程操作的安全问题

从上面结果可以看出当对线程任务添加锁后,即对两个线程操作的共同代码块同步以后,就可以办证单例在多线程操作中的安全。