前段时间有个面试,面试官问我 Spring 中的 Bean 是不是线程安全的,当时有点懵逼,主要是觉得线程安全和是不是 Spring 的 Bean 没关系,脑子一下没转过弯,所以回答了一些不着边际的问题。刚下楼溜了一圈,正好回想到这个问题,试着重新回答一下。
要回答什么是线程安全,那就必须聊一下什么是线程不安全。
个人觉得线程不安全要满足以下几个条件:
-
多线程,这个是肯定的,单线程有个屁的线程安全问题。
-
多个线程之间有共享资源,换句话说有任务需要它们协同完成。这点是最重要的,多线程编程正是为了利用多核并行的能力提升解决问题的效率,但协同工作带来的线程安全问题也随之而来。
-
共享资源有写操作,将共享资源改为只读是解决线程安全问题的一种思路。
-
写操作不是原子性,即使是最简单的一行 Java 代码,翻译成汇编也可能是多条,所以一般而言大多数单行代码都不是原子性的。但也有一些基础类型的操作是原子性的,但是这需要高超的汇编技巧,此外这么做并没有什么好处,而且极度依赖于特定平台,很可能换一台机器运行代码就会翻译成多条汇编,变成线程不安全的。
-
写操作不是独占的,这也是最基础的解决线程安全的思路,即在执行写操作时给资源加锁,阻止其它线程对该资源的读写,修改完资源后再去除锁。
好了,个人觉得以上就是线程安全问题的全部了,如果满足了上边所有条件,那就是线程不安全的,反之,就是线程安全的。
至于 Spring Bean,依然遵循上边的规则,并没有什么特殊情况。只不过在基于 Spring 的 Web 开发中,绝大多数我们打交道的 Spring Bean 都是单例(比如各种 Controller),这些单例会被大量并发的 HTTP 请求复用,也就是说基本满足上边说的第一条和第二条。不过这些单例使用的数据来源基本是 Redis 或者数据库,再就是 MQ 之流。显然这些成熟的中间件无一不是为了应对大规模的并发请求而生的,所以必然都是线程安全的。所以一般来说这些 Spring Bean 是线程安全的。但是,如果你添加了一个线程不安全的成员变量,且存在共享的非独占写操作,就可能满足上边所说的所有条件,那就会变成线程不安全。
文章评论