前言
当我们了解了 多线程生成的原因 之后,会有相应的解决办法,最典型的就是 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()
写在最后
- 写出来,说出来才知道对不对,知道不对才能改正,改正了才能成长。
- 在技术方面,希望大家眼里都容不得沙子。如果有不对的地方或者需要改进的地方希望可以指出,万分感谢。
0 0