文章简介
本文主要介绍CAS实现自定义独占锁,及模拟高并发测试检测独占锁是否线程安全。本文更偏向于后者。
独占锁
CAS+BQ+LockSupport就可以完成,这里不是重点,重点是测试。
public class ExclusiveLock {
AtomicReference<Thread> sign = new AtomicReference();
BlockingQueue<Thread> waiter = new LinkedBlockingQueue();
public void lock(){
Thread thread = Thread.currentThread();
while(!sign.compareAndSet(null,thread)){
waiter.add(thread);
//这里不可以用wait notify,因为notify不能唤醒指定的线程,只能用LockSupport
LockSupport.park();
waiter.remove(thread);
}
}
public void unlock(){
if(sign.compareAndSet(Thread.currentThread(),null)){
Object[] arrs = waiter.toArray();
for (Object obj:arrs) {
LockSupport.unpark((Thread)obj);
}
}
}
}
错误测试
这段代码虽然跑起来没有问题,每次都是10000,但是却不是真正意义的并发,因为每次for循环也是有先后顺序的,哪怕只差那么几ns,而真正的并发是同时。
static int i;
static ExclusiveLock lock = new ExclusiveLock();
public static void main(String[] args) throws InterruptedException {
int concurrent=10000;
for(int i=0;i<concurrent;i++){
new Thread(new Runnable() {
@Override
public void run(){
add();
}
}).start();
}
Thread.sleep(4000);
System.out.println(i);
}
public static void add(){
lock.lock();
i++;
lock.unlock();
}
CountDownLatch测试
这种方式接近完美了,可是Thread.sleep(4000);这行代码是我所不能容忍的,我怎么知道到底要休眠多久。
那我们可以这样想,可不可以把CountDownLatch初始值定为concurrent+1,然后把Thread.sleep(4000);这行代码换成cdl.await();答案是不行的,因为当countDown到0的时候这10000+1个线程是同时运行的,所以打印出的值是一个很小的值。
我们接着想,可不可以在add执行完后用某个东西把他阻塞掉,等到10000个线程全部把add执行完才恢复。
请看下一个完美的案例 CountDownLatch+CyclicBarrier
static int i;
static ExclusiveLock lock = new ExclusiveLock();
public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
int concurrent=10000;
final CountDownLatch cdl = new CountDownLatch(concurrent);
for(int i=0;i<concurrent;i++){
new Thread(new Runnable() {
@Override
public void run() {
try {
cdl.await();//10000个线程全阻塞在这里,直到countDown到0为止
add();//10000个线程同时自增
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
cdl.countDown();
}
Thread.sleep(4000);//?
System.out.println(i);
}
public static void add(){
lock.lock();
i++;
lock.unlock();
}
CountDownLatch+CyclicBarrier测试
static int i;
static ExclusiveLock lock = new ExclusiveLock();
public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
int concurrent=10000;
final CyclicBarrier barrier = new CyclicBarrier(concurrent+1);//主线程也需要等待,所以+1
final CountDownLatch cdl = new CountDownLatch(concurrent);
for(int i=0;i<concurrent;i++){
new Thread(new Runnable() {
@Override
public void run() {
try {
cdl.await();//10000个线程全阻塞在这里,直到countDown到0为止
add();//10000个线程同时自增
barrier.await();//执行完后等待
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
cdl.countDown();
}
barrier.await();//
System.out.println(i);
}
public static void add(){
lock.lock();
i++;
lock.unlock();
}