java-可重入锁和不可重入锁

港控/mmm° 2023-10-07 18:02 100阅读 0赞

可重入锁(递归锁)

指的是以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的。

synchronized 和 ReentrantLock 都是可重入锁。

可重入锁的意义之一在于防止死锁。

实现原理实现是通过为每个锁关联一个请求计数器和一个占有它的线程。当计数为0时,认为锁是未被占有的;线程请求一个未被占有的锁时,JVM将记录锁的占有者,并且将请求计数器置为1 。

如果同一个线程再次请求这个锁,计数器将递增;

每次占用线程退出同步块,计数器值将递减。直到计数器为0,锁被释放。

不可重入锁

  1. 所谓不可重入锁,即若当前线程执行某个方法已经获取了该锁,
  2. 那么在方法中尝试再次获取锁时,就会获取不到被阻塞。(同一把锁)

实现

  1. package com.zm.demo6;
  2. import java.util.concurrent.TimeUnit;
  3. import java.util.concurrent.atomic.AtomicReference;
  4. public class MyLockTest {
  5. AtomicReference<Thread> atomicReference = new AtomicReference<>();
  6. // 加锁
  7. public void myLock() {
  8. // 获取当前线程的引用,既代码段正在被哪一个线程调用
  9. Thread thread = Thread.currentThread();
  10. System.out.println(Thread.currentThread().getName() + "==> mylock");
  11. // 自旋锁 CAS实现
  12. while (!atomicReference.compareAndSet(null, thread)) {
  13. }
  14. }
  15. // 解锁
  16. public void myUnLock() {
  17. Thread thread = Thread.currentThread();
  18. System.out.println(Thread.currentThread().getName() + "==> myUnlock");
  19. atomicReference.compareAndSet(thread, null);
  20. }
  21. public static void main(String[] args) throws InterruptedException {
  22. // 底层使用的自旋锁CAS
  23. MyLockTest lock = new MyLockTest();
  24. new Thread(() -> {
  25. lock.myLock();
  26. try {
  27. TimeUnit.SECONDS.sleep(3);
  28. } catch (Exception e) {
  29. e.printStackTrace();
  30. } finally {
  31. lock.myUnLock();
  32. }
  33. }, "T1").start();
  34. TimeUnit.SECONDS.sleep(1);
  35. new Thread(() -> {
  36. lock.myLock();
  37. try {
  38. TimeUnit.SECONDS.sleep(3);
  39. } catch (Exception e) {
  40. e.printStackTrace();
  41. } finally {
  42. lock.myUnLock();
  43. }
  44. }, "T2").start();
  45. }
  46. }

验证

  1. public static void main(String[] args) throws InterruptedException {
  2. MyLockTest myLockTest = new MyLockTest();
  3. b(0,myLockTest);
  4. }
  5. /**
  6. * 同一把锁
  7. * @param i
  8. * @param lock
  9. */
  10. private static void b(int i,MyLockTest lock){
  11. lock.myLock();
  12. try {
  13. i++;
  14. System.out.println(i);
  15. if (i == 10) {
  16. return;
  17. }
  18. b(i,lock);
  19. } catch (Exception e) {
  20. e.printStackTrace();
  21. } finally {
  22. lock.myUnLock();
  23. }
  24. }

无法打印1-10
在这里插入图片描述

可重入锁

  1. import java.util.concurrent.TimeUnit;
  2. import java.util.concurrent.atomic.AtomicReference;
  3. public class MyLockTest {
  4. AtomicReference<Thread> atomicReference = new AtomicReference<>();
  5. private volatile int count = 0;
  6. // 加锁
  7. public void myLock() {
  8. // 获取当前线程的引用,既代码段正在被哪一个线程调用
  9. Thread thread = Thread.currentThread();
  10. System.out.println(Thread.currentThread().getName() + "==> mylock" + count);
  11. if(atomicReference.get()==thread){
  12. count++;
  13. return;
  14. }
  15. // 自旋锁 CAS实现
  16. while (!atomicReference.compareAndSet(null, thread)) {
  17. }
  18. }
  19. // 解锁
  20. public void myUnLock() {
  21. Thread thread = Thread.currentThread();
  22. System.out.println(Thread.currentThread().getName() + "==> myUnlock" + count);
  23. if(count > 0){
  24. count--;
  25. }else {
  26. atomicReference.compareAndSet(thread, null);
  27. System.out.println(1111);
  28. }
  29. }
  30. }
  31. public static void main(String[] args) throws InterruptedException {
  32. MyLockTest myLockTest = new MyLockTest();
  33. b(0,myLockTest);
  34. //a();
  35. }
  36. 这是一个带volatile前缀的int值,是一个类似计数器的东西。
  37. 可以用来表示该锁被线程重入的次数。
  38. count0表示该锁不被任何线程持有;当count1表示线程恰好持有该锁1次(未重入);
  39. count大于1则表示锁被线程重入count次。
  40. 因为这是一个会被并发访问的量,为了防止出现可见性问题要用volatile进行修饰。

ReentrantLock 的源码

链接: 从源码角度彻底理解ReentrantLock(重入锁).

发表评论

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

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

相关阅读