《Java 拓展读物 线程安全问题》
作者:谎言诞行    发布时间:2026年01月16日

长久以来,“线程安全”问题一直困扰着很多Java程序员。那么,什么是“线程安全”问题呢?其实,这个东西是一个翻译错误。在英文上,它叫 thread-save 。这单词的话,直译当然是叫“线程安全”。但是,实际上应该叫“资源竞争”问题,更为合适。因为,它是多个线程在并行访问一个共享资源时,引发的资源信息异常问题。

在解析这个问题前,我们需要先了解计算机内部处理器(CPU)的调度机制才行。在计算机内部,中央处理器(CPU)一般就是一个。当然,有些服务器有多个,但是那些情况不影响我们的讨论。既然CPU资源有限,要处理的程序任务有那么多,那大家就排队吧。因此,CPU资源调度算法就出来了,它一般称为“时间片”算法。因为,普遍来说都是按照时间切片,分配给不同的“进程”。

这就好比,大家一起去饭店吃饭。店里面桌子的位置有1座,有2座,有3座。空闲时,当然是随便进去,但是繁忙时期,就要等了。在里面空出一桌1个座位的话,那就只能让一个人进去吃饭了。如果你是3-4人组团吃饭,那就要等3座以上的桌子清空了。因此,很多人在饭点吃饭,都是在门口等位。

“线程”这个东西,其实就是在“进程”的下面再细分资源。比如:你们3个人一起组团吃饭。店里面分配了一个桌子给你们。你们这个团队就是一个“进程”。这个桌子分配给你们用了,至于你们怎么用餐,店家是不管的。你们单独的每个人,就是一个单独的“线程”。你们可以一起下单,也可以单独点餐,这桌子和饭店都是共享的。所以,“线程”只是“进程”下面的一个细分任务而已。但是,你只能在你的桌子用餐,不能影响其它人。

对于计算机来说,从来就没有空闲的时候。电脑一启动,一堆系统进程要运行,它们还派生了很多线程出来。这CPU就像一座永远都要等位的饭店。所以,这就出现了一个名词“线程休眠”。我们的线程,在等待吃饭时,就是休眠状态的。与之相关的资源,也是一起休眠,保存起来,等待下一次执行。对于CPU来说,你的程序每执行一条指令,就需要去CPU吃一次饭。一个程序的执行,要在“执行”和“休眠”之间不停往复,直到程序全部结束。

在明白程序的执行过程、CPU时间片分配之后,我们再来讨论线程安全问题。由于线程在执行任务时,会不停“休眠”和“执行”。如果你的某个数据,是多个线程“协同读、写”的话,那么数据可能无法保证准确。简单来说,一旦你的程序是“多个线程协作”,那么它们可不会按照顺序执行的。因为,CPU的资源调度是不可预知的。下面,我们举一个例子。

假设共享数值S=1,让线程A,B,C一起去读取,并回写+1的结果。虽然,你代码是按顺序写的,但是程序不一定按顺序执行。如果,A线程休眠时间长,那么B、C就先读取了。所以,A读到的不一定是1,可能是2,可能3。这个就是“线程安全”问题。这问题,目前就2个解决方法:“不共享”或者“共享时加锁”。

不共享资源,这个当然简单。所以,我们主要讨论“加锁”的情况。如果我们对程序加锁,一定要A的操作结束,才让其它线程操作,那么一切都很美好。但是,这就涉及另外一个问题“性能瓶颈”。万一A的操作要休眠很久呢?这不是要排队排到月球了吗?所以,现在根据业务不同,出现了很多不同的策略。比如:写操作加锁,读操作不加锁。当然,还有目前很流行的“区块锁”。就是,在数据的对应区块上加锁。不管其它区块怎么读写,你的锁影响不到其它区块的数据。

总的来说,线程安全问题,就是资源竞争的问题。不要总是复制别人的方案,要根据自己实际需要,写自己的资源处理策略

填写一种颜色的字符(不区分大小写),1分钟有效,点击图片刷新