Java提供了基本类型的封装类(Integer、Double),但是,在并发情况下,没办法保证线程安全。所以,还提供了AtomicInteger, AtomicLong 等具有线程安全的封装类。
1、什么是线程安全
在多线程环境中,"线程安全" 指的是当多个线程同时访问某个公共资源时,能够确保数据的正确性和一致性,避免出现死锁问题。
2、AtomicInteger类的特点
原子性:指一个操作要么完全执行,要么完全不执行,期间不会被其他线程的操作打断。
①原子操作
AtomicInteger 提供了多种原子操作方法,这些方法能够在不使用显式同步(如 synchronized 关键字)的情况下保证操作的原子性。比如,incrementAndGet() 方法会先将当前值加一然后返回更新后的值,这一系列操作是不可分割的,不会被其他线程的操作打断。
②CAS(Compare-And-Swap)机制
AtomicInteger 内部依赖于 CAS 操作来实现原子性。CAS 是一种乐观锁策略,它检查内存位置的值是否与预期值相匹配,如果匹配则更新该值;如果不匹配,则重试直到成功为止。
③无阻塞算法
相比传统的基于锁的同步机制(synchronized),AtomicInteger 使用的 CAS 操作是非阻塞的。
3、代码示例
以 AtomicInteger 和 Integer 作为公共资源,开启两个线程进行计数。
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicExample {
private static AtomicInteger count = new AtomicInteger(0);
private static Integer num = 0;
public static void main(String[] args) throws InterruptedException {
//开启两个线程,对 AtomicInteger类、 Integer类 分别递增
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 25; i++) {
num = i + 1;
System.out.println("当前线程 " + Thread.currentThread().getName() + " 执行操作,获取的Integer值:" + num);
count.incrementAndGet();
System.out.println("当前线程 " + Thread.currentThread().getName() + " 执行操作,获取的AtomicInteger值:" + count.get() + "\n");
}
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 25; i++) {
num = i + 1;
System.out.println("当前线程 " + Thread.currentThread().getName() + " 执行操作,获取的Integer值:" + num);
count.incrementAndGet();
System.out.println("当前线程 " + Thread.currentThread().getName() + " 执行操作,获取的AtomicInteger值:" + count.get() + "\n");
}
});
//调用start()方法,实现真正的多线程运行,会自动执行run()方法
t1.start();
t2.start();
//如果子线程执行了耗时操作,不加join()方法的话,可能主线程结束的时候,子线程还没有结束运算,
// 这个时候,主线程获取子线程计算的值是不完整的
// 而调用join方法后,主线程就会等待子线程结束之后,才结束
t1.join();
t2.join();
System.out.println("AtomicInteger 计算结束的值: " + count.get());
System.out.println("Integer 计算结束的值: " + num);
}
}
start()方法,开启当前线程,会自动执行线程的run()方法
join()方法,保证子线程执行完毕,主线程才会执行join()后面的业务代码。因为我需要拿到子线程计算后的值,所以才调这个方法,一般是用不上这个方法的。
控制台打印结果:
可以看见 ,多线程环境下,Integer类型无法保证线程安全。