1、什么是Atomic
Atomic英译为原子的。原子结构通常称为不可分割的最小单位。而在JUC中,java.util.concurrent.atomic 包是 Java 并发库中的一个包,提供了原子操作的支持。它包含了一些原子类,用于在多线程环境下进行线程安全的原子操作。使用原子类可以避免使用锁和同步机制,从而减少了线程竞争和死锁的风险,并提高了多线程程序的性能和可伸缩性。
2、为什么要使用Atomic
这里是JUC专栏,肯定是跟多线程有关系的。我们实现这样一个场景:2个线程对某个数值+1操作,每个线程累加10000次。
2.1、传统模式
package atomic;import java.util.concurrent.atomic.AtomicInteger;/*** @author Shamee loop* @date 2023/5/22*/
public class AtomicTest {private static int counter = 0;public static void main(String[] args) {// 多个线程并发地增加计数器的值Thread thread1 = new Thread(() -> {for (int i = 0; i < 10000; i++) {counter++;}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 10000; i++) {counter++;}});thread1.start();thread2.start();thread1.join();thread2.join();System.out.println("Counter value: " + counter);}
}
执行结果:
不管运行多少次,每次的结果都不一样,而且最终的值大概率都不会是20000。而20000才是我们期望的输出。
2.2、原子模式
package atomic;import java.util.concurrent.atomic.AtomicInteger;/*** @author Shamee loop* @date 2023/5/22*/
public class AtomicTest {// 原子性intprivate static AtomicInteger counter = new AtomicInteger(0);public static void main(String[] args) {// 多个线程并发地增加计数器的值Thread thread1 = new Thread(() -> {for (int i = 0; i < 10000; i++) {// 等同于++操作counter.incrementAndGet();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 10000; i++) {counter.incrementAndGet();}});thread1.start();thread2.start();thread1.join();thread2.join();System.out.println("Counter value: " + counter.get());}
}
运行结果:
不管运行多少次,结果都是20000。可以看出代码中不需要加任何锁,Atomic在多线程场合天然的具备线程安全。
3、原子类
java.util.concurrent.atomic包下常用的原子类分为:
3.1、原子基本类型
- AtomicInteger:整形原子类
- AtomicLong:长整型原子类
- AtomicBoolean :布尔型原子类
3.2、原子引用类型
- AtomicReference:引用类型原子类
3.3、原子数组类
- AtomicIntegerArray:整形数组原子类
- AtomicLongArray:长整形数组原子类
- AtomicReferenceArray :引用类型数组原子类
......
4、原子类是绝对线程安全吗?
首先,什么是线程安全问题?
在多线程编程中,线程安全是指多个线程同时访问一个共享资源时,不会产生不正确的结果或破坏数据结构的属性。
其实Atomic的原子性是指属性的存取(get/set)方法是线程安全,他的线程安全保证并不是简单的使用synchronized或lock锁。而是使用了乐观锁CAS(Compare And Swap,比较并交换)+volatile。关于CAS,可以参考《简单理解CAS》。正如CAS锁的ABA问题,它并不能保证对象是线程安全的。
因此Atomic并不是绝对的线程安全。
在多线程编程中,"atomic"操作通常被认为是一种细粒度的同步机制,用于保护共享数据的访问和修改。它们通常比其他同步机制(如锁)的开销更小,并且可以提供一定程度的线程安全性。
"atomic"操作的行为因编程语言和上下文而异,以下是一些常见情况和注意事项:
- 原子读取(Atomic Reads):"atomic"操作可以确保从共享变量中读取的值是最新的。这意味着一个线程在读取共享变量时,不会看到另一个线程修改变量后的旧值。
- 原子写入(Atomic Writes):"atomic"操作可以确保将值写入共享变量时的原子性。这意味着一个线程在写入共享变量时,不会被其他线程的读取或写入操作中断或干扰。
- 原子递增和递减(Atomic Increment/Decrement):某些编程语言提供原子递增和递减操作,以确保对共享计数器的操作是线程安全的。这些操作会在执行过程中阻止其他线程的干扰。
尽管"atomic"操作提供了一定的线程安全性,但在处理复杂的并发场景时,仍然需要考虑其他因素,如数据竞争、同步机制的选择和使用正确的内存模型等。此外,"atomic"操作并不能解决所有的线程安全问题,如死锁、竞争条件等。
因此,如果在特定的编程语言或框架中使用"atomic"操作,建议查阅相关文档和规范,以了解其具体行为和适用范围。同时,仍然需要谨慎设计和编写多线程代码,以确保整个程序的线程安全性。