ReentrantLock

92 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第20天,点击查看活动详情

当多个线程需要访问某个公共资源的时候,我们知道需要通过加锁来保证资源的访问不会出问题。java提供了两种方式来加锁:

  • 一种是关键字: synchronized,一种是concurrent包下的基于API实现的。
  • synchronized是 JVM底层支持的,而concurrent包则是 jdk实现。

公平锁和非公平锁

  • 当有线程竞争锁时,当前线程会首先尝试获得锁而不是在队列中进行排队等候,这对于那些已经在队列中排队的线程来说显得不公平,这也是非公平锁的由来
  • 默认情况下为非公平锁。
  • 锁的存储结构就两个东西:”双向链表” + “int类型状态”。ReenTrantLock的实现是一种自旋锁, 通过循环调用CAS操作来实现加锁。它的性能比较好也是因为避免了使线程进入内核态的阻塞状态。想尽办法避免线程进入内核的阻塞状态是我们去分析和理解锁设计的关键钥匙。

image.png

  • fair:该参数为true时,会尽力维持公平

image.png

lock

  • 正常的获取锁,如果没有获得到锁,就会被阻塞

lockInterruptibly

  • 获取锁,如果没有获得到锁,就会被阻塞
  • 可以被打断

tryLock

  • 如果获得到锁,返回true
  • 如果没有获得到锁,返回false
  • timeout:表示等待的时间
  • tryLock()在获取的锁的时候,不会考虑此时是否有其他线程在等待,会破坏公平。
  • 如果你希望遵守公平设置此锁,然后用 tryLock(0, TimeUnit.SECONDS) 这几乎是等效的(它也检测中断)。

unLock

  • 尝试释放此锁。
  • 必须是锁的持有者才能释放锁

getOwner

  • 返回持有锁的线程

hasQueuedThreads

  • 是否有线程在等待获取锁

getQueueLength

  • 获取等待锁的线程数目

getQueuedThreads

  • 返回正在等待的线程集合

Lock和synchronized的区别

底层实现

  • Lock基于 AQS实现,通过state和一个CLH队列来维护锁的获取与释放
  • synchronized需要通过 monitor,经历一个从用户态到内核态的转变过程,更加耗时

其他区别

synchronizedLock
是java内置关键字,在jvm层面是个java类
无法判断是否获取锁的状态可以判断是否获取到锁
会自动释放锁需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁
线程会一直等待下去如果尝试获取不到锁,线程可以不用一直等待就结束

总结来说
synchronized的锁可重入、不可中断、非公平。而Lock锁可重入、可判断、可公平(两者皆可)。