显式锁

Java锁分为:
1、内置锁:
synchronized、volatile
2、显式锁:
JDK1.5引入了显式锁:Lock及其子类(如:ReentrantLock、ReadWriteLock等)

两者区别:
1)可中断申请:
synchronized申请一个内置锁时,如果该锁被其它线程持有,则当前线程会阻塞,而且阻塞期间无法中断,而显式锁提供了可中断申请。
例子:
import  java.util.concurrent.locks.Lock;
import  java.util.concurrent.locks.ReentrantLock;
public  class  Test  extends  Thread {
     private  static  Lock lock =  new  ReentrantLock();
     @Override
     public  void  run() {
         try  {
             // 以可中断方式申请锁, 在申请锁的过程中如果当前线程被中断,将抛出InterruptedException异常
             lock.lockInterruptibly();
         catch  (InterruptedException e) {
             System.out.println( "interruption happened" );
             return ;
         }
         // 如果运行到这里, 说明已经申请到锁, 且没有发生异常(没有被中断)
         try  {
             System.out.println( "run is holding the lock" );
         finally  {
             lock.unlock();
         }
     }
     public  static  void  main(String[] args)  throws  InterruptedException {
         try  {
             lock.lock(); //主线程一开始就持有锁,所以子线程申请锁的时候被阻塞
             Thread thread =  new  Test();
             thread.start();
             // 1s后中断thread线程, 该线程此时应该阻塞在lockInterruptibly方法上
             Thread.sleep( 1000 );
             // 中断thread线程将导致其抛出InterruptedException异常.
             thread.interrupt();
             Thread.sleep( 1000 );
         finally  {
             lock.unlock();
         }
     }
}

2)尝试型申请
lock.tryLock和lock.tryLock(long time, TimeUnit unit)方法用于尝试获取锁。如果尝试没有成功,则返回false,否则返回true,而内置锁则不提供这种特性,一旦开始申请内置锁,在申请成功之前,线程无法中断,申请也无法取消。Lock的尝试型申请通常用于实现时间限定的task。
尝试型申请也可以被中断!
3)锁的释放

对于内置锁,只要代码运行到同步代码块之外,就会自动释放锁,开发者无需担心抛出异常,方法返回等情况发生时锁会没有被释放的问题。然而对于显式锁,必须调用unlock方法才能释放锁。此时需要开发者自己处理抛出异常, 方法返回等情况。通常会在finally代码块中进行锁的释放, 还需注意只有申请到锁之后才需要释放锁, 释放未持有的锁可能会抛出未检查异常。

所以使用内置锁更容易一些, 而显式锁则繁琐很多. 但是显式锁释放方式的繁琐也带来一个方便的地方: 锁的申请和释放不必在同一个代码块中,但必须在同一个线程中,不可以释放别的线程持有的锁。

4)公平锁

通过ReentrantLock(boolean fair)构造函数创建ReentrantLock锁时可以为其指定公平策略, 默认情况下为不公平锁.

多个线程申请公平锁时, 申请时间早的线程优先获得锁. 然而不公平锁则允许插队, 当某个线程申请锁时如果锁恰好可用, 则该线程直接获得锁而不用排队. 比如线程B申请某个不公平锁时该锁正在由线程A持有, 线程B将被挂起. 当线程A释放锁时, 线程B将从挂起状态中恢复并打算再次申请(这个过程需要一定时间). 如果此时恰好线程C也来申请锁, 则不公平策略允许线程C立刻获得锁并开始运行. 假设线程C在很短的一段时间之后就释放了锁, 那么可能线程B还没有完成恢复的过程. 这样一来, 节省了线程C从挂起到恢复所需要的时间, 还没有耽误线程B的运行. 所以在锁竞争激烈时, 不公平策略可以提高程序吞吐量.

内置锁采用不公平策略, 而显式锁则可以指定是否使用不公平策略

5)唤醒和等待(同步)

线程可以wait在内置锁上, 也可以通过调用内置锁的notify或notifyAll方法唤醒在其上等待的线程。但是如果有多个线程在内置锁上wait, 我们无法精确唤醒其中某个特定的线程。

显式锁也可以用于唤醒和等待。调用lock.newCondition()方法可以创建一个Condition对象,调用condition.await()方法将使得线程等待,调用condition.singal()或condition.singalAll()方法可以唤醒在该condition对象上等待的线程。由于同一个显式锁可以创建多个Condition对象,不同的线程可以在不同的Condition对象上等待,因此我们可以实现精确唤醒某个线程。大致用法:

创建Condition对象:

private  Condition c1 = lock.newCondition();  
private  Condition c2 = lock.newCondition();  
private  Condition c3 = lock.newCondition(); 
线程1可以调用下面语句等待:

c1.await();

线程2可以调用下面语句等待:

c2.await();

线程3可以调用下面语句等待:

c3.await();
可以调用下面语句分别对1、2、3三个线程唤醒
c1.signal();
c2.signal();
c3.signal();
与内置锁一样,如果唤醒信号在等待之前发出,则该唤醒信号被丢弃。
注意:
wait、await、notify、signal等这些操作不是为了获取锁,也不是为了互斥,而是为了同步,即在 已经持有锁的情况下,进一步需要某些条件才能往下执行,这时调用wait或await等待条件,等待条件的时候,会释放持有的锁,这样别的线程便可以获取锁,从而调用对应的notify或signal来唤醒这些等待的线程,被唤醒的线程重新获取锁之后,沿着等待的位置往下执行。所以,上面的这些操作需要在获取锁之后,否则报错,如, wait必须要在synchronized块里,c1.await()首先该线程要获得lock这把锁,否则也是会报错的。
















版权声明:本文为博主原创文章,未经博主允许不得转载。

转载于:https://my.oschina.net/u/1987489/blog/491363

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值