单例模式和线程安全问题

单例模式和线程安全问题

单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。

​ 许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。

基本的实现思路

单例模式要求类能够有返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称)。

单例的实现主要是通过以下两个方式:

  1. 将该类的构造方法定义为私有方法,这样其他处的代码就无法通过调用该类的构造方法来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例;
  2. 在该类内提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。

单例模式的代码体现

饿汉模式

public class Ehan{
    //私有化本类构造方法
    private Ehan(){}
    //静态变量创建对象
    private static Ehan instance= new Ehan();
    //对外提供获取对象的方法
    public static Ehan getInstance(){
        return instance;
    }
}

懒汉模式

public class LanHan{
    //私有化本类构造
    private LanHan(){};
    //创建对象
    private static LanHan instance=null;
    //对外提供静态访问方法,获取本类实例对象
    public static LanHan getInstance(){
        if(instance == null )  // 这里会有线程安全问题
		{
			instance = new Single();
		}
		return instance;
    }
}

懒汉模式它的特点是运行时获得对象的速度比较慢,但加载类的时候比较快。它在整个应用的生命周期只有一部分时间在占用资源

饿汉模式它的特点是加载类的时候比较慢,但运行时获得对象的速度比较快。它从加载到应用结束会一直占用资源

这两种模式对于初始化较快,占用资源少的轻量级对象来说,没有多大的性能差异,选择懒汉式还是饿汉式都没有问题。但是对于初始化慢,占用资源多的重量级对象来说,就会有比较明显的差别了。所以,对重量级对象应用饿汉模式,类加载时速度慢,但运行时速度快;懒汉模式则与之相反,类加载时速度快,但运行 时第一次获得对象的速度慢。

从用户体验的角度来说,我们应该首选饿汉模式。我们愿意等待某个程序花较长的时间初始化,却不喜欢在程序运行时等待太久,给人一种反应迟钝的感觉,所以对于有重量级对象参与的单例模式,我们推荐使用饿汉模式

懒汉模式的并发问题

//饿汉式 多线程并发饿汉式没问题。多线程在执行getInstance()方法时不进行对象的创建。
class Single
{
	private static final Single s = new Single();

	private Single(){}

	public static Single getInstance()
	{
		return s;
	}
}
//懒汉式。
class Single
{
	private static  Single s = null;
	private Single(){}
	/*
	并发访问会有安全隐患,多个线程可能同时进行if(s==null)判断并创建多个对线,所以加入同步机制     解决安全问题。
	但是,同步的出现降低了效率。
	可以通过双重判断的方式,解决效率问题,减少判断锁的次数。
	*/
	public static Single getInstance()
	{
		if(s==null)
		{
			synchronized(Single.class)//-->B线程,等着A解锁才让进去
			{
				if(s==null) s = new Single();
			}
		}
		return s;
	}
}

​ 当s == null时,A线程进来了,它加了一下锁后进入第二个if(s ==null){},然后沉睡;此时,B也通过了第一个 if(s == null),当B玩下执行时,遇到了synchronized(),发现这里A进行了加锁,没办法B线程只能等着,等A把锁解了。此时,A线程醒来了,它new 了一个对象后,继续玩下执行,然后把锁解了,这时s不等于null了。B发现A解锁了,它继续往下执行,发现s不等于null了,那它直接返回了A创建的那个对象s。当C线程访问getSingle方法时,只需判断s是否为null,而不用去判断锁对象了。因为s不等于null了,所以直接返回对象,这样就提高了效率。

懒汉模式是延迟加载的实例,面对多线程访问时,需要进行同步代码块,为了增加效率,又要使用双重判断

如果没有第一个if,那么多线程访问时,每个线程都需要去判断锁,而双重判断模式下,更多机会只需判断if条件,相比较判断锁更有效率

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值