天天看点

ReentrantLock和synchronized两种锁定机制

reentrantlock和synchronized两种锁定机制

把代码块声明为 synchronized,使得该代码具有 原子性(atomicity)和 可见性(visibility)。

原子性意味着一个线程一次只能执行由一个指定监控对象(lock)保护的代码,从而防止多个线程在更新共享状态时相互冲突。

可见性类似volatile关键字。

reentrantlock 类实现了 lock ,它拥有与 synchronized 相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能(换句话说,当许多线程都想访问共享资源时,jvm 可以花更少的时候来调度线程,把更多时间用在执行线程上)。

可重入锁 reentrantlock 的含义是当某个线程获取某个锁后,在未释放锁的情况下,第二次再访问该锁锁定的另一代码块时,可以重新进入该块。

reentrantlock典型的使用方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

<code>class</code> <code>x {</code>

<code>   </code><code>private</code> <code>final</code> <code>reentrantlock lock = </code><code>new</code> <code>reentrantlock();</code>

<code>   </code><code>// ...</code>

<code>   </code><code>public</code> <code>void</code> <code>m() {</code>

<code>     </code><code>lock.lock();  </code><code>// block until condition holds</code>

<code>     </code><code>try</code> <code>{</code>

<code>       </code><code>// ... method body</code>

<code>     </code><code>} </code><code>finally</code> <code>{</code>

<code>       </code><code>lock.unlock();</code>

<code>     </code><code>}</code>

<code>   </code><code>}</code>

<code> </code><code>}</code>

  

(1)什么情况下可以使用 reentrantlock

使用synchronized 的一些限制: 

无法中断正在等候获取一个锁的线程;

无法通过投票得到一个锁;

释放锁的操作只能与获得锁所在的代码块中进行,无法在别的代码块中释放锁;

reentrantlock 没有以上的这些限制,且必须是手工释放锁。

(2)简单对比

主要相同点:lock能完成synchronized所实现的所有功能

主要不同点:lock有比synchronized更精确的线程语义和更好的性能,当许多线程都在争用同一个锁时,使用 reentrantlock 的总体开支通常要比 synchronized 少得多。 

synchronized会自动释放锁,而lock一定要求程序员手工释放,并且必须在finally从句中释放。

一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线

程必须等待当前线程执行完这个代码块以后才能执行该代码块。 

二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized

(this)同步代码块。 

三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)

同步代码块的访问将被阻塞。 

四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个

object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。 

五、以上规则对其它对象锁同样适用

(1)用法区别

synchronized(隐式锁):在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。

lock(显示锁):需要显示指定起始位置和终止位置。一般使用reentrantlock类做为锁,多个线程中必须要使用一个reentrantlock类做为对 象才能保证锁的生效。且在加锁和解锁处需要通过lock()和unlock()显示指出。所以一般会在finally块中写unlock()以防死锁。

(2)synchronized和lock性能区别

synchronized是托管给jvm执行的,而lock是java写的控制锁的代码。

synchronized采用的是cpu悲观锁机制,即线程获得的是独占锁。独占锁意味着其 他线程只能依靠阻塞来等待线程释放锁。独占锁意味着其他线程只能依靠阻塞来等待线程释放锁。而在cpu转换线程阻塞时会引起线程上下文切换,当有很多线程竞争锁的时候,会引起cpu频繁的上下文切换导致效率很低。

lock用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁实现的机制就 是cas操作(compare and swap)。

(3)synchronized和lock用途区别

synchronized原语和reentrantlock在一般情况下没有什么区别,但是在非常复杂的同步应用中,请考虑使用reentrantlock,特别是遇到下面2种需求的时候。

1.某个线程在等待一个锁的控制权的这段时间需要中断

2.需要分开处理一些wait-notify,reentrantlock里面的condition应用,能够控制notify哪个线程

3.具有公平锁功能,每个到来的线程都将排队等候、

如果我们把每个线程理解成 一个个门,门都需要上锁,

在没有reentrantlock之前,我们上锁,可能统一用sycnchronized(钥匙),大家都在竞争锁钥匙,钥匙只有一把,谁先拥有谁就先开门进去,门打不开的就一直卡死等待,浪费时间,还不能干其他事情,就消耗在这里了

有了reentrantlock,英文解释可重用锁, 就不需要钥匙了,我们可以灵活的为一组门(thread)配置一把特殊的锁,为另一组门配置另外一把锁,多灵活啊,这把锁仍然拥有synchronized的功能,

1.如果用trylock(非阻塞),此次获取不到锁,那你也不会等待,可以在门口玩会手机,或者去超时买菜,一会再来trylock一次 这个方法更灵活;

2.如果用lock(阻塞),那就跟synchronized一样的,获取了锁,就开门了,同一时刻 其他的门就死等,不能干其他事情;