前面我们介绍过并发编程三大问题:操作原子性、数据可见性、操作有序性,解决操作原子性问题的基本方法是线程同步。不过Java中,如果对于类似i += 1
这类操作,其实除了使用线程同步我们还有更好的办法。JDK提供了一系列原子类,我们可以直接使用这些原子类进行原子操作,此外还提供了累加器,可以性能更好的方式实现累加操作。
原子类相比线程同步,具有以下优势:
JDK提供了一组基本类型原子类,常用的包括AtomicLong
、AtomicInteger
、AtomicBoolean
等,下面我们以AtomicInteger
为例进行介绍。
package com.gacfox.demo;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
private static final Random random = new Random();
private static final AtomicInteger atomicInteger = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[100];
// 创建100个线程对atomicInteger进行操作
for (int i = 0; i < 100; i++) {
threads[i] = new Thread(() -> {
try {
// 为了让并发运行效果更明显,这里让线程随机睡一会
Thread.sleep(random.nextInt(100));
atomicInteger.getAndIncrement();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
threads[i].start();
}
// 等待所有线程执行完
for (int i = 0; i < 100; i++) {
threads[i].join();
}
// 输出atomicInteger值
System.out.println(atomicInteger.get());
}
}
上面代码我们创建了一个AtomicInteger
对象,然后创建了100个线程对其进行加1的操作,最终我们可以看到atomicInteger
的值为100,这是因为atomicInteger.getAndIncrement()
是一个原子操作。但如果上面代码将AtomicInteger
改为普通的int
,那结果就不好说了,我们得到的结果将是随机的。
除了基本类型的原子类,JDK还提供了数组、引用等原子类,具体可以参考文档,这里就不多介绍了。
对于累加操作,我们可以直接使用累加器代替原子类加n的写法,累加器底层进行了优化,对累加操作比原子类直接加n性能更好,下面例子以LongAdder
为例进行介绍。
package com.gacfox.demo;
import java.util.Random;
import java.util.concurrent.atomic.LongAdder;
public class Main {
private static final Random random = new Random();
private static final LongAdder longAdder = new LongAdder();
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[100];
// 创建100个线程对longAdder进行操作
for (int i = 0; i < 100; i++) {
threads[i] = new Thread(() -> {
try {
// 为了让并发运行效果更明显,这里让线程随机睡一会
Thread.sleep(random.nextInt(100));
longAdder.increment();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
threads[i].start();
}
// 等待所有线程执行完
for (int i = 0; i < 100; i++) {
threads[i].join();
}
// 输出longAdder值
System.out.println(longAdder.longValue());
}
}
代码和之前使用原子类差不多,这里我们调用longAdder.increment()
这个原子方法,最终输出结果为100。