文章目录
i++问题
i++和++i在这个问题上是一样的。
一个简单的 i++ 操作,看似简单,但是它的底层是由三条 CPU 指令完成的,但正是 i++ 是没有原子性确定的,在底层运行当中就可能会有 CPU 的调度产生,造成 i 的值被修改,造成脏读脏写。
volatile 是不能解决这个问题的,因为 i++ 不安全是因为原子性的问题,volition 只能保证线程的可见性和有序性。
在解决这个问题的时候,可以使用 synchronized 或者 ReentrantLock 都是可以解决这个问题的,在这一般使用的是 synchronized 更好,因为 JVM 在版本更新迭代的时候就一直在优化改进这个机制,是可以尽早的获得更好的性能,并且synchronized 对大多数开发人员来说更加熟悉,方便代码的阅读。
戳这里:了解synchronized和ReentrantLock 解决原子性问题
也可以使用 AtomicInteger 来解决这个问题。AtomicInteger 类的底层实现原理利用处理器的 CAS 操作(Compare And Swap,比较与交换,一种无锁的算法)
戳这里:具体了解CAS是什么
用 CAS 来检测栈中的值是否被其他线程所改变,如果被改变则 CAS 操作失败。这种实现方法在CPU指令级别实现了原子操作。
因此,使用 CAS 是比 synchronized 来实现同步效率更高的。
AtomicInteger
AtomicInteger 是 Integer 类提供的原子操作。常见的原子操作类还有 AtomicBoolean、AtomicLong、AtomicReference等,它们的实现原理是相同的,区别就在于运算的对象的类型不同。
还可以通过 AtomicReference< V > 将一个对象的所有操作都转换为原子操作。
AtomicInteger 的性能通常是 synchronized 和 ReentrantLock 的好几倍。
而 AtomicInteger 的具体用法:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
AtomicIntegerTest test = new AtomicIntegerTest();
FutureTask<AtomicInteger> task = new FutureTask<>(test);
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
System.out.println(task.get());//获得线程里面最后返回的值
}
}
class AtomicIntegerTest implements Callable<AtomicInteger> {
//定义一个原子操作
AtomicInteger num = new AtomicInteger(0);
@Override
public AtomicInteger call() {
for (int i = 0; i < 1000000; i++) {
num.getAndIncrement(); //通过原子操作数执行自增操作
}
return num;
}
}