单例模式的线程安全问题

(自我学习)
来源:99°的开水https://www.cnblogs.com/lnzr/p/4187404.html

单例会带来什么问题?
如果多个线程同时调用这个实例,会有线程安全的问题

单例一般用在什么地方?
单例的目的是为了保证运行时只有唯一的一个实例,最常用的地方比如拿到数据库的连接,或者Spring的中创建BeanFactory操作,而这些操作都是调用他们的方法来执行某个特定的动作。

首先先来认识下两种模式: 恶汉式 懒汉式

public class MyFactory {

// //饿汉式 立即创建
// private static MyFactory instance = new MyFactory();
// public static MyFactory getInstance(){
// return instance;
// }

//懒汉式用到的时候在创建,不用就不创建(有线程安全问题)
private static MyFactory instance = null;
public static MyFactory getInstance(){

instance = new MyFactory();return instance;
}
}

下面总结一下解决线程安全的几种方式:
方法一:在MyFactory 中加入了一个私有静态内部类instanceHolder ,对外提供的接口是 getInstance()方法,也就是只有在MyFactory.getInstance()的时候,instance对象才会被创建,,没有使用同步。保证了只有一个实例,还同时具有了Lazy的特性

public class MyFactory {
private static class instanceHolder {
public static MyFactory instance = new MyFactory();
}

    public static MyFactory getInstance() {   
        return MyFactory.instanceHolder.instance;   
    }   

}

测试代码

import static org.junit.Assert.*;
import org.junit.Test;
public class MyFactoryTest {
@Test
public void testGetResource() {
MyFactory mf1 = MyFactory.getInstance();
MyFactory mf2 = MyFactory.getInstance();
System.out.println(mf1 != null);//true
System.out.println(mf1 == mf2);//true
}
}

方式二:(懒汉式)
这种方式也没有使用同步,并且确保了调用static getInstance()方法时才创建MyFactory的引用,

private static MyFactory instance = new MyFactory();

public static MyFactory getInstance() {
  return instance;
}

测试代码:同方法一

方式三:使用synchronized ,通常会锁定整个方法的是比较耗费资源的,实际会产生多线程访问问题的是这一句代码instance = new MyFactory();
为了减少资源的消耗,只锁这一句就行了, 两个线程并发地进入第一次判断instance是否为空的if 语句内部,一个线程获得了锁执行new操作,另一个线程被阻塞,
当第一个线程执行完毕之后,第二个线程如果直接进行new操作也是不安全的。为了避免第二次new操作,添加第二次条件判断,既二次检查

private static MyFactory instance;
public static MyFactory getInstance(){
    if(instance == null){
        synchronized (MyFactory.class) {
            if(instance == null){
                instance = new MyFactory();
            }
        }
    }
    return instance;
}    

测试代码 同方法一

在Spring中,Bean的默认作用域是单例模式,即每个容器只会创建一个实例。对于单例模式的Bean,如果多个线程同时访问该Bean的方法或属性,可能会引发线程安全问题。这是因为多个线程共享同一个实例,对实例的修改可能会相互干扰。因此,需要特别注意在单例模式下处理线程安全问题。 对于解决单例模式下的线程安全问题,可以采取以下几种方式: 1. 方法级别加锁:在需要保证线程安全的方法上使用synchronized关键字,确保同一时间只能有一个线程访问该方法。这种方式简单直接,但会降低并发性能。 2. 使用ThreadLocal:ThreadLocal是Java提供的一种线程级别的变量隔离机制。可以将需要共享的对象存储在ThreadLocal中,每个线程都拥有自己的副本,避免了线程安全问题。可以将非线程安全的对象存储在ThreadLocal中,确保每个线程都使用自己的对象副本。 3. 将Bean的作用域设置为prototype:使用prototype作用域的Bean在每次被注入或获取时都会创建一个新的实例,解决了单例模式下的线程安全问题。可以通过在Bean的配置文件中设置scope属性为"prototype"来实现。 总结起来,为了解决Spring Bean单例模式下的线程安全问题,可以采用方法级别加锁、使用ThreadLocal或将Bean的作用域设置为prototype等方式来保证线程安全性。具体选择哪种方式需要根据实际场景和需求来决定。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值