JUC 即 java.util.concurrent 包,提供了大量的工具类来简化并发编程。
Atomic类是一种原子类型数据,主要用以解决数值的递增的并发问题。在传统的解决方案中,需要对数值操作代码区进行保护,保护手段一般是加锁。而Atomic类提供了incrementAndGet方法,其内部用了cas操作,直接无锁的操作往上递增,效率大大提升。
1. 原始的synchronized方法
package com.concurrent.juc.ch01;
import java.time.LocalDateTime;
public class ConditionRaceExp {
private static volatile int cnt = 0;
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[1000];
for(int i=0;i<threads.length;i++){
threads[i] = new Thread(()->{
synchronized(ConditionRaceExp.class) {
for (int k = 0; k < 10000; k++) {
cnt++;
}
}
});
}
long start = System.currentTimeMillis();
for(int i=0;i<threads.length;i++){
threads[i].start();
}
for(int i=0;i<threads.length;i++){
threads[i].join();
}
long end = System.currentTimeMillis();
System.out.println(end-start);
System.out.println(cnt);
}
}
2. 使用原子类 AtomicLong
package com.concurrent.juc.ch01;
import java.util.concurrent.atomic.AtomicLong;
public class AtomicTest {
static AtomicLong cnt = new AtomicLong(0l);
static void m() {
for (int i = 0; i < 10000; i++)
cnt.incrementAndGet();
}
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[1000];
for (int i = 0; i < threads.length; i++)
threads[i] = new Thread(() -> {
AtomicTest.m();
});
long start = System.currentTimeMillis();
for(int i=0;i<threads.length;i++){
threads[i].start();
}
for(int i=0;i<threads.length;i++){
threads[i].join();
}
long end = System.currentTimeMillis();
System.out.println(end-start);
System.out.println(AtomicTest.cnt.get());
}
}
3. 使用原子类 使用LongAdder
package com.concurrent.juc.ch01;
import java.util.concurrent.atomic.LongAdder;
public class LongAdderTest {
static LongAdder cnt = new LongAdder();
static void m() {
for (int i = 0; i < 10000; i++)
cnt.increment();
}
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[1000];
for (int i = 0; i < threads.length; i++)
threads[i] = new Thread(() -> {
AtomicTest.m();
});
long start = System.currentTimeMillis();
for(int i=0;i<threads.length;i++){
threads[i].start();
}
for(int i=0;i<threads.length;i++){
threads[i].join();
}
long end = System.currentTimeMillis();
System.out.println(end-start);
System.out.println(AtomicTest.cnt.get());
}
}
总结
atomic类的原理是CAS,因此不会消耗操作系统锁资源。
LongAdder的内部做了一个分段锁,线程会分配到不同的段内,在计算值时再进行累积。
因此这两个方法比较适合此类场景。当然使用synchronized方法也是可以的。在粗化锁的适合,速度甚至快于前两种方法,但是在细化锁的时候效率就显得低下了。
多线程系列在github上有一个开源项目,主要是本系列博客的实验代码。
https://github.com/forestnlp/concurrentlab
如果您对软件开发、机器学习、深度学习有兴趣请关注本博客,将持续推出Java、软件架构、深度学习相关专栏。