JAVA高性能编程之线程安全原子性浅析

线程安全原子性问题

什么是线程安全

当多个线程访问某一个类时,
不管运行时环境采用何种调度方式或者这些进程将如何交替执行,
并且在主调代码中不需要任何额外的同步或协同,
这个类都能表现出正确的行为,那么就称为是线程安全的。
线程安全的操作其中一个特性就是原子性。

怎样的操作会产生原子性问题

可看如下代码
JAVA高性能编程之线程安全原子性浅析
多次执行这段代码可以发现,执行结果会产生小于60000的情况,结果表明i++这个操作并不是个原子性的操作,
那么原因在哪呢?
用javap命令编译Counter可得到字节码命令
JAVA高性能编程之线程安全原子性浅析
由代码可以看到。i++操作分为4个步骤 首先当有线程执行时
1 线程会开辟独有的工作空间,并执行getfield,从堆内存中取出i的值。
2 执行iconst-1 将i的值写入操作数栈。
3执行iadd,进行累加操作。
4putfield,将操作数栈中的值写回堆内存中。
当只有一个线程执行时,操作没有问题,但同时有多个线程请求堆内存中i的值的时候,会导致请求到相同的i值,
导致该次执行失效。所以执行上面代码时会产生小于60000的情况

什么样的操作是原子操作

原子操作可以是一个步骤,也可以是多个步骤,但顺序不可以被打乱,也不可以被切割而只执行其中一部分
将整个操作视为一个整体。资源在该次操作中保持一致。这是原子性的核心特征

怎么实现原子操作

1 可以通过加锁的方式,即通过synchronized加锁,也可以是ReentrantLock加锁.
这种方式的原子操作,锁定了资源,使得整个操作变为单线程的串行操作,效率不高
synchronized 加锁
public class Counter {

volatile int i = 0;

public synchronized void increament() {
i++;
}
}
ReentrantLock 加锁
public class Counter {

volatile int i = 0;

Lock lock = new ReentrantLock();

public void increament() {
lock.lock();
i++;
lock.unlock();
}
}

2 通过JDK自带的AtomicInteger 实现原子操作,底层是由CAS实现,仍然使用多线程进行,效率会提高很多
AtomicInteger 实例
public class Counter {

AtomicInteger i= new AtomicInteger();

public void increament() {
i.incrementAndGet();
}
}