Java 多线程:Lock 关键字

来源:互联网 发布:linux运维 编辑:程序博客网 时间:2024/06/05 08:01

前言


当我们了解了 多线程生成的原因 之后,会有相应的解决办法,最典型的就是 synchronized 和 lock。lock可以说是 synchronized 的一个替代品,synchronized 能做的事,lock 基本都可以做,而且能做得更好。他们的一些区别是:

  • lock在获取锁的过程可以被中断。
  • lock可以尝试获取锁,如果锁被其他线程持有,则返回 false,不会使当前线程休眠。
  • lock在尝试获取锁的时候,传入一个时间参数,如果在这个时间范围内,没有获得锁,那么就是终止请求。
  • synchronized 会自动释放锁,lock 则不会自动释放锁。

这样可以看到,lock 比起 synchronized 具有更细粒度的控制。但是也不是说 lock 就完全可以取代 synchronized,因为 lock 的学习成本,复杂度等方面要比 synchronized 高,对于初级 java 程序员,使用 synchronized 的风险要比 lock 低。

目录


包括了:

  • Lock 接口方法分析
  • RentrantLock
  • ReadWriteLock

Java Lock接口源码分析


Lock 接口方法如下:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">interface</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Lock</span> {</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> lock();    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> lockInterruptibly() <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> InterruptedException;    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> tryLock();    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> tryLock(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> time, TimeUnit unit) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> InterruptedException;    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> unlock();    Condition newCondition();}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li></ul>

lock,unlock 方法

lock() 可以用于对一段代码进行加锁,这样别的代码在锁释放之前需要进行等待,需要注意,lock不会像 synchronized 那样自动释放锁,所以:一定要放在 try-finally块中,保证锁的释放。 例如:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">lock</span>.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">lock</span>();    ......} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">finally</span> {    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">lock</span>.unlock();  }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul>

tryLock 方法

  • tryLock():尝试获得锁,如果成功,返回 true,否则,返回 false。
  • tryLock(long time,TimeUnit unit):在一定的时间内尝试获得锁,并且在这段时间直接可以被打断。如果成功获得,那么将返回 true,否则,返回 false。

lockInterruptibly 方法

这里首先需要了解两个概念才能更好的理解这个方法:

  • 线程的打扰机制
  • Thread类的interrupt,interrupted,isInterrupted方法的区别

对于线程的打扰机制,每个线程都有一个打扰标志。

  • 如果线程在sleep或wait,join,此时如果别的进程调用此进程的 interrupt()方法,此线程会被唤醒并被要求处理InterruptedException;
  • 如果线程在运行,则不会收到提醒。但是 此线程的 “打扰标志”会被设置。

所以说,对于 interrupt() 方法:

  • 不会中断一个正在运行的线程
  • 不会中断一个正在运行的线程
  • 不会中断一个正在运行的线程

对于 interrupt,interrupted,isInterrupted方法的区别:

interrupt 方法上面有说到了。对于 interrupted 和 isInterrupted 方法,stackoverflow 说得很好了:

<code class="hljs oxygene has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">interrupted() <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">is</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">and</span> checks the current thread. isInterrupted() <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">is</span> an instance <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">method</span> <span class="hljs-title" style="box-sizing: border-box;">which</span> <span class="hljs-title" style="box-sizing: border-box;">checks</span> <span class="hljs-title" style="box-sizing: border-box;">the</span> <span class="hljs-title" style="box-sizing: border-box;">Thread</span> <span class="hljs-title" style="box-sizing: border-box;">object</span> <span class="hljs-title" style="box-sizing: border-box;">that</span> <span class="hljs-title" style="box-sizing: border-box;">it</span> <span class="hljs-title" style="box-sizing: border-box;">is</span> <span class="hljs-title" style="box-sizing: border-box;">called</span> <span class="hljs-title" style="box-sizing: border-box;">on</span>.<span class="hljs-title" style="box-sizing: border-box;">A</span> <span class="hljs-title" style="box-sizing: border-box;">common</span> <span class="hljs-title" style="box-sizing: border-box;">error</span> <span class="hljs-title" style="box-sizing: border-box;">is</span> <span class="hljs-title" style="box-sizing: border-box;">to</span> <span class="hljs-title" style="box-sizing: border-box;">call</span> <span class="hljs-title" style="box-sizing: border-box;">a</span> <span class="hljs-title" style="box-sizing: border-box;">static</span> <span class="hljs-title" style="box-sizing: border-box;">method</span> <span class="hljs-title" style="box-sizing: border-box;">on</span> <span class="hljs-title" style="box-sizing: border-box;">an</span> <span class="hljs-title" style="box-sizing: border-box;">instance</span>.<span class="hljs-title" style="box-sizing: border-box;">Thread</span> <span class="hljs-title" style="box-sizing: border-box;">myThread</span> = ...;</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (myThread.interrupted()) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">{}</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// WRONG! This might not be checking myThread.</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (myThread.isInterrupted()) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">{}</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Right!</span>Another difference <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">is</span> that interrupted() also clears the status <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> the current thread. <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">In</span> other words, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> you call it twice <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">in</span> a row <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">and</span> the thread <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">is</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">not</span> interrupted between the two calls, the second call will return <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span> even <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> the first call returned <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>.The Javadocs tell you important things like this; use them often!</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li></ul>

下面再介绍下 lockInterruptibly 方法:

当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状 态。例如当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那 么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。

newCondition()

用于获取一个 Conodition 对象。Condition 对象是比 Lock 更细粒度的控制。要很好的理解 condition,个人觉得必须要知道,生产者消费者问题。

简单来说就是,我们都了解 生成者在缓冲区满了的时候需要休眠,此时会再唤起一个线程,那么你此时唤醒的是生成者还是消费者呢,如果是消费者,很好;但是如果是唤醒生产者,那还要再休眠,此时就浪费资源了。condition就可以用来解决这个问题,能保证每次唤醒的都是消费者。具体参考:Java 多线程:condition关键字

lock 方法大体就介绍到这里。

ReentrantLock


可重入锁:指同一个线程,外层函数获得锁之后,内层递归函数仍有获得该锁的代码,但是不受影响。

可重入锁的最大作用就是 可以避免死锁。例如:A线程 有两个方法 a 和 b,其中 a 方法会调用 b 方法,假如 a,b 两个方法都需要获得锁,那么首先 a 方法先执行,会获得锁,此时 b方法将永远获得不了锁,b 方法将一直阻塞住, a 方法由于 b 方法没有执行完,它本身也 不释放锁,此时就会造成一个 死锁。 
ReentrantLock 就是一个可重入锁。真正使用 锁的时候,一般是 Lock lock = new ReentrantLock();然后 使用 Lock 接口方法。

ReadWriteLock


接口代码如下:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">interface</span> ReadWriteLock {      Lock readLock();      Lock writeLock();  }  </code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>

ReadWriteLock 可以算是 Lock 的一个细分,合理使用有利于提高效率。比如说, 对于一个变量 i, A,B 线程同时读,那么不会造成错误的结果,所以此时是允许并发,但是如果是同时写操作,那么则是有可能造成错误。所以真正使用的时候,可以使用 细分需要的是读锁还是写锁,再相应地进行加锁。

Ps:从代码也可以看出,ReadWriteLock 和 Lock 没有关系,既不继承,也不是实现。

参考


  • Java 理论与实践: JDK 5.0 中更灵活、更具可伸缩性的锁定机制
  • Java中Lock,tryLock,lockInterruptibly有什么区别?
  • Thread类的interrupt,interrupted,isInterrupted方法的理解
  • Java多线程sleep(),join(),interrupt(),wait(),notify()

写在最后


  1. 写出来,说出来才知道对不对,知道不对才能改正,改正了才能成长。
  2. 在技术方面,希望大家眼里都容不得沙子。如果有不对的地方或者需要改进的地方希望可以指出,万分感谢。
0 0
原创粉丝点击