对于涉及共享变量访问的操作,若该操作从其执行线程以外的任意线程来看是不可分割的,那么该操作就具有原子性。原子操作是多线程环境下的一个概念,它是针对访问共享变量的操作而言的。原子操作的“不可分割”包括以下两层含义:
- 读写某个共享变量的操作从其执行线程以外的任何线程来看,该操作要么已经执行结束要么尚未发生。
- 访问同一组共享变量的原子操作是不能够被交错的。
理解原子操作这个概念还需要注意以下两点:
- 原子操作是针对访问共享变量的操作而言的。也就是说仅涉及局部变量访问的操作无所谓是否是原子的,或者干脆把这一类操作都看成原子操作。
- 原子操作是从该操作的执行线程以外的线程来描述的,也就是说它只有在多线程环境下有意义。换言之单线程环境下一个操作无所谓是否具有原子性。
总的来说,Java中有两种方式来实现原子性:
- 使用锁(Lock):锁具有排他性,它能够保障一个共享变量在任意一个时刻只能够被一个线程访问,这就排除了多个线程在同一时刻访问同一个共享变量导致干扰与冲突的可能,即消除了竞态。
- 利用cpu提供的专门CAS指令:CAS指令实现原子性的方式与锁实现原子性的方式实质上是相同的,差别在于锁通常是在软件这一层次实现的,而CAS是直接在硬件这一层次实现的。
Java语言中,long和double以外的任何类型(包括引用类型)的变量的写操作都是原子操作,这点由Java语言规范规定,由JVM具体实现。而对于long和double型变量通过使用volatile关键字保证其写操作具有原子性。Java语言中对任何变量的读操作都具有原子性。
volatile仅能够保障变量写操作的原子性,它并不能保障其他操作(比如read-modify-write和check-then-act)的原子性。