栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring 单例 bean 的线程安全问题

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring 单例 bean 的线程安全问题

首先解释一下什么是单例 bean?

单例的意思就是说在 Spring IoC 容器中只会存在一个 bean 的实例,无论一次调用还是多次调用,始终指向的都是同一个 bean 对象

用代码来解释单例 bean

public class UserService {
    public void sayHello() {
        System.out.println("hello");
    }
}



		
    


public class Demo {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans-singleton.xml");

        UserService service = context.getBean(UserService.class);
        UserService service1 = context.getBean(UserService.class);
        System.out.println(service == service1);
    }
}

运行 main 方法最后会输出:true,这就很明显的说明了无论多少次调用 getBean 方法,最终得到的都是同一个实例。

把上面 xml 文件的配置修改一下,修改为:


然后再次运行 main 方法,结果输出:false,说明两次调用 getBean 方法,得到的不是同一个实例。


了解了什么是单例 bean 之后,我们继续来说说单例 bean 的线程安全问题

为什么会存在线程安全问题呢?

因为对于单实例来说,所有线程都共享同一个 bean 实例,自然就会发生资源的争抢。

用代码来说明线程不安全的现象

public class ThreadUnSafe {

    public int i;

    public void add() {
        i++;
    }

    public void sub() {
        i--;
    }

    public int getValue() {
        return i;
    }
}



    


public class Demo {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans-singleton.xml");

        for (int j = 0; j < 10; j++) {
            new Thread(() -> {
                ThreadUnSafe service = context.getBean(ThreadUnSafe.class);
                for (int i = 0; i < 1000; i++) {
                    service.add();
                }
                for (int i = 0; i < 1000; i++) {
                    service.sub();
                }
                System.out.println(service.getValue());
            }).start();
        }
    }
}

上面的代码中,创建了 10 个线程来获取 ThreadUnSafe 实例,并且循环 1000 次加法,循环 1000 次减法,并把最后的结果打印出来。理想的情况是每个线程打印出来的结果都是 0

先看一下运行结果:

2073
1736
1080
1060
221
49
50
-231
-231
-231

从结果可以看出,运行结果都不是 0,这明显的是线程不安全啊!

为什么会出现这种情况?

因为 10 个线程获取的 ThreadUnSafe 实例都是同一个,并且 10 个线程都对同一个资源 i 发生了争抢,所以才会导致线程安全问题的发生。

现在把 xml 文件中的配置做一下更改:scope 的值改为 prototype




    
    


然后再次运行 main 方法,发现无论运行多少次,最后的结果都是 0,是线程安全的!

因为 prototype 作用域下,每次获取的 ThreadUnSafe 实例都不是同一个,所以自然不会有线程安全的问题。


如果单例 bean 是一个无状态的 bean,还会有线程安全问题吗?

不会,无状态 bean 没有实例对象,不能保存数据,是不变类,是线程安全的。

public class ThreadSafe {

    public void getValue() {
        int val = 0;
        for (int i = 0; i < 1000; i++) {
            val++;
        }
        for (int i = 0; i < 1000; i++) {
            val--;
        }
        System.out.println(val);
    }
}
public class Demo {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans-singleton.xml");

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                ThreadSafe service = context.getBean(ThreadSafe.class);
                service.getValue();
            }).start();
        }
    }
}

运行结果为 0

事实证明,无状态的 bean 是线程安全的。(无状态 bean 应该是这个意思,如有不对的地方,还望指出)


那么针对单例 bean,而且是有状态的 bean,应该如何保证线程安全呢?

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/293184.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号