主要有两种实现,悲观锁和乐观锁实现方式.
悲观的解决方案(阻塞同步)
我们知道,num++看似简单的一个操作,实际上是由1.读取 2.加一 3.写入 三步组成的,这是个复合类的操作(所以我们之前提到过的volatile是无法解决num++的原子性问题的),在并发环境下,如果不做任何同步处理,就会有线程安全问题。最直接的处理方式就是加锁。
synchronized(this){
num++;
}
使用独占锁机制来解决,是一种悲观的并发策略,每次操作数据的时候都认为别的线程会参与竞争修改,所以直接加锁。同一刻只能有一个线程持有锁,那其他线程就会阻塞。线程的挂起恢复会带来很大的性能开销,尽管jvm对于非竞争性的锁的获取和释放做了很多优化,但是一旦有多个线程竞争锁,频繁的阻塞唤醒,还是会有很大的性能开销的。所以,使用synchronized或其他重量级锁来处理显然不够合理。
方案一:ReentrantLock方式实现,100个并发任务基于10个线程执行,每个任务循环执行10次计数.
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockTest {
private static int count = 0;
private static final Lock CAS_LOCK=new ReentrantLock();
private static final int JOB_NUM=100;
private static final int THREAD_SIZE=10;
private static void service() {
CAS_LOCK.lock();
try {
count++;
System.out.println("thread num execute:"+Thread.currentThread().getName()+":"+count);
} finally {
CAS_LOCK.unlock();
}
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(THREAD_SIZE);
CountDownLatch countDownLatch = new CountDownLatch(JOB_NUM);
for(int i=0;i<JOB_NUM;i++){
executorService.execute(new Runnable() {
// 每个线程执行10次计算
@Override
public void run() {
for(int j=0;j<10;j++){
try {
service();
} finally {
countDownLatch.countDown();
}
}
}
});
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
executorService.shutdown();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("get Count:"+count);
}
}
执行结果如下:
方案一:synchionized方式实现,100个并发任务基于10个线程执行,每个任务循环执行10次计数.
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ReentrantLockTest {
private static volatile int count = 0;
private static final int