原子性说明

1. java多线程中的原子性的理解

众所周知,原子是构成物质的基本单位(当然电子等暂且不论),所以原子的意思代表着——“不可分”。

由不可分性可知,原子性是拒绝多线程操作的,因此不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对它进行操作(其他线程的读操作也不行,因为要防止脏读)!

以创建对象为例:Person per = new Person();,看似一步创建对象的操作,但是对于计算机底层操作来说,这一步实际上可以分解成以下三个步骤:

  1. 分配内存空间
  2. 初始化对象
  3. 将对象指向刚分配的内存空间

但是需要注意,创建对象操作不是原子性操作,对于多线程操作,在一个线程创建对象执行上述三个步骤的过程中,可能会有其他线程来查询该对象的数据,但在该对象还没有彻底初始化完毕时就查询数据,可能查询到的数据是脏数据,不是最新数据。

下面说一下同步锁和原子操作的区别:

synchronized 是不是能够代替原子操作?不能, 因为synchronized同步代码块在执行的过程当中,它内部的变量可以被其他线程获取到。而如果用原子操作,当该变量被一个线程操作时,其他线程不能同时操作该变量(读操作也不行,因为要防止脏读)。这个区别就造成了,如果有需求要求synchronized同步代码块内的变量不能被其他线程获取到,此时就要用原子操作。 但是貌似这种需求比较少。

下面说一下java多线程下集合类不安全原因:List.add(),Set.add(),Map.add(),i++等,导致数据不安全的问题的原因解释如下:

以i++为例进行举例说明,其他同理

java中i++操作不具有原子性,不是原子操作,其实际上分为三步:

  1. 获得 i 值
  2. +1
  3. 写回这个值。
TimeThread AThread B
T1获得 i 值
T2获得 i 值
T3i–>+1
T4i–>+1
T5写回这个值
T6写回这个值

上表中线程A和线程B再各自的工作内存中得到的是相同的 i 值,因此经过这两个线程+1操作并写回主内存的是相同的值,A、B两个线程操作i++后 i 只是加了一次1,线程A的结果被线程B的结果覆盖了,这就是因为i++不具有原子性,而导致的数据安全问题。

解决上述问题可通过加锁或利用原子类来解决,对于集合类有CopyOnWriteArrayList, CopyOnWriteArraySet, ConcurrentHashMap分别对应List,Set,Map的三个类来解决集合类的多线程数据安全问题,而对于i++则可利用原子类AtomicInteger来解决,原子类底层用的是C++来直接操作内存并利用CAS的构成自旋锁,可通过查看原码得知。

java中的原子类具有原子性,其可保证原子性,java中的原子类在JUC包下:java.util.concurrent.atomic

2. 数据库事务中的原子性的理解

**事务举例:**A想要从自己的帐户中转1000块钱到B的帐户里。那么从A开始转帐,到转帐结束的这一个过程,称之为一个事务。在这个事务里,要做如下操作:

    1. 从A的帐户中减去1000块钱。如果A的帐户原来有3000块钱,现在就变成2000块钱了。
    1. 在B的帐户里加1000块钱。如果B的帐户如果原来有2000块钱,现在则变成3000块钱了。

如果在A的帐户已经减去了1000块钱的时候,忽然发生了意外,比如停电、断网什么的,导致转帐事务意外终止了,而此时B的帐户里还没有增加1000块钱。那么,我们称这个操作失败了,要进行回滚(ROLLBACK)。回滚就是回到事务开始之前的状态,也就是回到A的帐户还没减1000块的状态,B的帐户的原来的状态。此时A的帐户仍然有3000块,B的帐户仍然有2000块。

**事务的原子性:**如果把一个事务可看作是一个程序,它要么完整的被执行,要么完全不执行,而且一个事务在执行过程中,其他事务想要操作同一数据(读操作也不行,因为要防止脏读),就需要等待该事务执行完毕。

原子操作: 符合原子性的操作就是原子操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值