JUC之AbstractQueuedSynchronizer-自定义锁

水深无声 2022-10-15 04:47 192阅读 0赞

使用AbstractQueuedSynchronizer实现简单的自定义锁,为学习ReentrantLock做好铺垫。


(1)自定义实现类

MyLock通过继承AbstractQueuedSynchronizer,重写tryAcquire方法实现自定义获取锁的逻辑,重写tryRelease自定义实现释放锁的逻辑。设置state=1,即设置只有1把锁,每次同时只能有一个线程抢到这把锁,并且不支持重复获取。

  1. import java.util.concurrent.locks.AbstractQueuedSynchronizer;
  2. public class MyLock extends AbstractQueuedSynchronizer {
  3. /**
  4. * 初始化state的值为1,表示只有1把锁可以抢
  5. */
  6. public MyLock() {
  7. setState(1);
  8. }
  9. public void lock() {
  10. super.acquire(0);
  11. }
  12. public void unlock() {
  13. super.release(1);
  14. }
  15. /**
  16. * 分别重写tryAcquire、tryRelease方法自定义获取锁与释放锁的逻辑
  17. *
  18. */
  19. @Override
  20. protected boolean tryAcquire(int arg) {
  21. return compareAndSetState(1, 0);
  22. }
  23. @Override
  24. protected boolean tryRelease(int arg) {
  25. return compareAndSetState(0, 1);
  26. }
  27. }

(2)使用

count虽然使用了volatile保证线程可见性,但是count++不是原子性的,所以执行count++计算不是线程安全的。sell方法是普通方法,没有加锁来保证线程安全。sellWithLock方法使用自定义MyLock锁来保证线程安全。

使用1000个线程并发调用方法,对count++操作。如果满足线程安全,则最后打印的值必须为1000,否则不满足线程安全。

  1. import java.util.concurrent.CountDownLatch;
  2. public class Main {
  3. public static void main(String[] args) throws InterruptedException {
  4. Main main = new Main();
  5. /**
  6. * 创建1000个线程调用方法,执行count++
  7. */
  8. for (int i = 0; i < 1000; i++) {
  9. new Thread(()->{
  10. // main.sell();
  11. main.sellWithLock();
  12. countDownLatch.countDown();
  13. }).start();
  14. }
  15. countDownLatch.await();
  16. System.out.println("count的最后值:"+main.count);
  17. }
  18. private volatile int count = 0;
  19. private MyLock myLock = new MyLock();
  20. private static CountDownLatch countDownLatch = new CountDownLatch(1000);
  21. private void sellWithLock() {
  22. myLock.lock();
  23. try {
  24. Thread.sleep(10);
  25. } catch (Exception e) {
  26. e.printStackTrace();
  27. }
  28. count++;
  29. myLock.unlock();
  30. }
  31. private void sell() {
  32. // 这边一定要让线程睡眠一会,不然在for循环调用的时候,
  33. // 由于方法执行很快,会造成线程是一个一个依次执行的效果
  34. try {
  35. Thread.sleep(10);
  36. } catch (Exception e) {
  37. e.printStackTrace();
  38. }
  39. count++;
  40. }
  41. }

(代码写的很粗糙,凑合着看吧。)

示例中使用到了CountDownLatch,其实现也是依赖于AbstractQueuedSynchronizer。

发表评论

表情:
评论列表 (有 0 条评论,192人围观)

还没有评论,来说两句吧...

相关阅读