到此为止,并发编程的前三个系列基本结束。这三个系列的内容是紧密相关的。
线程之间的共享内存地址空间使得多个线程能够访问同一个共享变量,进而导致出现数据竞争的问题。为了解决数据竞争,保证共享变量在多个线程访问下的数据安全,我们引出了临界区的概念,让多个线程互斥的进入临界区是一个很容易想到的解决数据竞争的办法。
那么如何保证多个线程互斥的进入临界区呢?我们可以利用硬件提供的原子操作来是实现互斥锁(只是一种逻辑上的锁),在Intel硬件架构上,处理器提供了LOCK#语义保证了某个指令操作期间只有一个处理器能访问指定的内存位置,并且这个语义以LOCK前缀的形式暴露给了操作系统和软件,我们可以利用这些原子指令来实现一个逻辑上的互斥锁来实现互斥思想。
XCHG和LOCK CMPXCHG指令是两个很典型的能够实现互斥锁逻辑的指令,我们介绍了它们的语义和如何利用它们来实现互斥锁的逻辑。LOCK CMPXCHG指令还能不作为互斥锁而直接保证应用程序某些操作的线程安全,这是JDK中java.util.concurrent.atomic包下原子类的实现基础,通过分析它们的api,我们对JDK中的CAS有了很清晰的认识。
java.util.concurrent.atomic包下的原子类在进行更新操作(例如自增或者自减)时,会采用忙循环的方式直到更新成功,这种方式有优点也有缺点,实际上,这种方式(线程并没有阻塞)在很多场景下并不能满足需求,接下来的系列中,我们来看其他实现线程互斥的方式。