一男子给对象转账5000元,居然又退还了!
在并发编程中,所有问题的根源就是可见性、原子性和有序性问题,这篇文章我们就来聊聊原子性问题。
在介绍原子性问题之前,先来说下线程安全:
线程安全
我理解的线程安全就是不管单线程还是多线程并发的时候,始终能保证运行的正确性,那么这个类就是线程安全的。
其中在《Java并发编程实战》一书中对线程安全的定义如下:
当多个线程访问某个类时,不管运行是环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。
为了保证线程安全,可能会有很多的挑战和问题,当我们了解了问题根源所在,问题也就迎刃而解了,接下来介绍线程安全三大特性之一的原子性。
原子性
原子,我想大家应该都有印象吧,在化学反应中不可再分的基本微粒就是原子,也就是不可分割。
同时事务的四大特性 ACID 中也有原子性,那么原子性究竟是什么呢?
原子性其实就是所有操作要么全部成功,要么全部失败,这些操作是不可拆分的,也可以简单地理解为不可分割性。
将整个操作视作一个整体是原子性的核心特征,这些操作就是原子性操作。
接下来举个原子性操作在生活中的例子:
比如,wupx
今天刚发了 5100 元的工资,全身家当为 5100 元,huxy
目前余额还有 1000 元,此时 wupx
上交 5000 元,如果转账成功,则 huxy
的余额就变为了 6000 元,wupx
的余额为 100 元。
若转账失败,则转出去的余额会退回来,wupx
的余额仍然是 5100 元,huxy
的余额为 1000 元。
不会出现 wupx
的钱转出去了,huxy
的余额没有增加,或者 wupx
的工资没转出去,而 huxy
的余额却增加的情况。
wupx
上交工资给 huxy
的操作就是原子性操作,wupx
余额减少 5000 元,而 huxy
的余额增加 5000 元的操作是不可分割和拆分的,正如我们上面说到的:要么全部成功,要么全部失败。wupx
给 huxy
上交成功流程如下所示:
原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中的一部分。
到这里,我相信大家对原子性有了基本的了解,下面来聊下原子性问题。
原子性问题
原子性问题的核心就是线程切换导致的,因为并发编程中,线程数设置的数目一般会大于 CPU 核心数。
关于线程数的设置可以阅读:线程数,射多少更舒适?
每个 CPU 同一时刻只能被一个线程使用,而 CPU 资源分配采用的是时间片轮转策略,也就是给每个线程分配一个时间片,线程在这个时间片内占用 CPU 的资源来执行任务,当过了一个时间片后,操作系统会重新选择一个线程来执行任务,这个过程一般称为任务切换,也叫做线程切换或者线程上下文切换。
上图就是线程切换的例子,有 和 两个线程,其中粉色矩形表示该线程占有 CPU 资源并执行任务,刚开始 执行一段时间,这段时间称为时间
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
专注分享后端技术干货,包括 Java 基础、Java 并发、JVM、Elasticsearch、Zookeeper、Nginx、微服务、消息队列、源码解析、数据库、设计模式、面经等,助你编程之路少走弯路。