并发编程——6.线程的安全

本文详细阐述了线程安全的概念,介绍了线程不安全的示例,以及如何通过原子性、可见性、有序性保证线程安全。讨论了破坏临界资源的方法,如只读和局部变量,以及ThreadLocal和volatile的关键作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这篇文章我们主要来讲一下并发编程中的线程安全问题。

前面的几篇文章,我们讲解了什么是线程,以及关于线程性能的问题(即用线程池来避免重复创建线程造成资源损耗的问题),下面我们来了解一下线程安全的相关问题

目录

1.什么是线程安全

1.1相关介绍

1.2原子性

1.3可见性

1.4有序性

2.如何保证线程安全

2.1破坏临界资源

2.1.1只读

2.1.2局部变量

2.2ThreadLocal

2.2.1ThreadLocal及其底层原理和注意事项

2.2.2ThreadLocal的子类InheritableThreadLocal

2.3volatile

2.4原子类

3.小结


1.什么是线程安全

1.1相关介绍

要想知道什么是线程安全,就要知道什么是线程不安全

线程不安全:多线程下并发同时对共享数据进行读写,造成数据混乱的情况

举例说明:有一份商品,数量为10个,现在有并发的五个线程进行购买操作,首先每个线程读取商品数量即10,然后每个线程执行购买操作即商品数量减一,然后五个线程将操作完成的数据写进数据库即商品数量变为9,这明显是错误的,这就是线程不安全的情况。

线程安全就是为了避免线程不安全的情况出现。

当多线程并发访问临界资源时,如果破坏其原子性、可见性、有序性,可能会造成数据不一致。

临界资源:共享资源(同一对象)同时读写,一次仅允许一个线程使用,才可保证其正确性

下面用代码演示一下线程不安全的实例:

创建一个商品变量,初始值为1000000,然后线程执行的代码就是购买一个该商品。然后,再创建一个线程池,执行1000000个任务,然后打印输出剩余商品的数量。

如果是线程安全的,那么商品的剩余数量为0,下面看一下结果:

很明显商品数量不为0。这就是线程不安全的情况。

1.2原子性

前面说了,当多线程并发访问临界资源时,因为破坏其原子性、可见性、有序性,所以造成了数据不一致。下面讲一下什么是原子性

原子性操作指相应的操作是单一不可分割的操作。在我们学化学这门课程的时候,对于里面讲到的原子性相信大家都非常明白,原子是微观世界中最小的不可再进行分割的单元,原子是最小的粒子。java里面的原子性操作也是如此,它代表着一个操作不能再进行分割是最小的执行单元,或者一系列操作要么全部成功执行,要么全部执行失败,不允许中间某一些成功失败,类比如事物控制,要么全部提交要么全部回滚。

下面根据几个例子来分析下原子性操作:

i = 0 ;
j = i ;
i++ ;
i = j + 1 ;

上面四句代码,只有第一句是原子性操作,其余三句都不是

再看我们前面举的例子:

正是因为 stock-- 这句操作不是原子性操作,所以才出现线程不安全的情况。

那怎么保证原子性操作呢?我们可以通过锁、synchronized来确保,具体的我们后面会讲。

1.3可见性

下面讲一下可见性

可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改后的值。

下面来看一下例子:

初始 always 为 true,然后子线程根据always的值进行循环,主线程先睡眠2s,然后修改always的值。

正常情况下,2s过后,always的值被修改,然后子线程的循环结束,代码结束。下面我们来看一下运行结果:

显然,上面的程序是有问题的,是线程不安全的。

为什么会出现一直运行的情况呢?分析情况可以推理出来:子线程没有读取到被修改后的always值。为什么没读取到?这就涉及到JMM模型,下面根据上面的例子

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

L提笔画长安

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值