Java多线程(十八)---Java中的锁---Lock接口

移步java多线程系列文章

1.内部锁的不足

  • 不可中断:使用内部锁(指的是synchronized)时,不能中断正在等待获取锁的线程
  • 不可超时:使用内部锁时,在请求锁失败情况下,必须无限等待,没有超时效果
  • 自动释放:使用内部锁时,内部锁必须在获取它们的代码块中被自动释放(虽然对代码来说是种简化且对异常友好)
  • 不可伸缩:使用内部锁时,无法细粒度控制锁(伸缩性不足),即无法实现锁分离和锁联结,比如为每个链表节点(或部分)加锁从而允许不同的线程能够独立操作链表的不同节点(部分),遍历或修改链表时,需先获取该节点锁并直到获取下一个节点锁时才释放当前节点锁
  • 性能问题:使用内部锁时,在有竞争情况下仍会出现性能问题,尽管JDK6对内部锁进行了优化,但无论是偏向锁或是轻量级锁都是针对无竞争情况的优化,无竞争情况下与ReentractLock性能一致,但有竞争时Lock明显更高效

2.Lock接口综述

  • 定义: JDK1.5引入Lock接口,其定义了一些抽象的锁操作,相比synchronized,Lock提供了无条件、可轮询、可定时、可中断的锁获取操作,所有加锁和解锁的方法都是显式的
  • 实现: Lock的实现必须提供具有与synchronized相同的内存语义,但加锁的语义、调度算法、顺序保证、性能特性可以有所不同
  • 使用: Lock接口的实现基本是通过聚合一个同步器AbstractQueuedSynchronized的子类来完成线程的访问控制
  • 对比内部锁: Lock缺少隐式获取/释放锁的便捷,但却拥有了锁获取与释放的可操作性、可中断的获取锁以及超时获取锁等多种内部锁不具备的同步性,甚至还支持读写锁分离,同时允许获取和释放可以不在同一个块中
  • 并发包中的Lock接口实现类,其中ReadLock和WriteLock是ReentrantReadWriteLock的静态内部类
Lock接口实现类.png

3.Lock接口方法标准使用

//标准用法
Lock lock = new ReentrantLock();
//获取锁应在try之前,因为若获取锁时发生异常,异常抛出同时会导致锁无故释放
lock.lock();
try{
    doSometing();
}finally{
    //注意:必须在finally块中释放锁,目的是保证在获取到锁之后,最终能被释放
    lock.unlock();
}

4.Lock接口重点方法

4.1 lock方法

  • lock方法应具有与内部锁加锁相同的内存语义,即无锁阻塞和支持可重入
  • lock方法必须搭配unlock方法使用,同时必须在finally中显式调用unlock方法释放锁
/**
 * Acquires the lock.
 *  获取锁,调用该方法的当前线程将会获取锁,当锁获得后,从该方法返回
 * <p>If the lock is not available then the current thread becomes
 * disabled for thread scheduling purposes and lies dormant until the
 * lock has been acquired.
 *  若当前锁不可用(已被占有),当前线程会一直休眠直到锁为可被获取状态
 * <p><b>Implementation Considerations</b>
 *  实现该方法的注意事项
 * <p>A {@code Lock} implementation may be able to detect erroneous use
 * of the lock, such as an invocation that would cause deadlock, and
 * may throw an (unchecked) exception in such circumstances.  The
 * circumstances and the exception type must be documented by that
 * {@code Lock} implementation.
 *  该方法的实现需要能发现lock被错误使用,如死锁或抛出不可查异常(即可运行期异常和Error)
 *  此时该实现必须用文档注明其可能出现的异常或需要的使用环境
 */
void lock();

4.2 lockInterruptibly方法

  • lockInterruptibly方法提供可中断的锁获取操作并允许在可取消的活动中使用

  • 一个可中断的锁获取操作的标准使用范式如下:

public boolean doTask throws InterruptedException(){
    lock.lockInterruptibly();
    try{
        return cancelTask();
    }finally{
        lock.unlock();
    }
}
//取消任务
private boolean cancelTask() throws InterruptedException {...}
/**
 * Acquires the lock unless the current thread is
 * {@linkplain Thread#interrupt interrupted}.
 *      可中断地获取锁,即在锁的获取中可以中断当前线程
 * <p>Acquires the lock if it is available and returns immediately.
 *  当获取锁时锁可用就立即返回
 * <p>If the lock is not available then the current thread becomes
 * disabled for thread scheduling purposes and lies dormant until
 * one of two things happens:
 * <ul>
 * <li>The lock is acquired by the current thread; or
 * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
 * current thread, and interruption of lock acquisition is supported.
 * </ul>
 *  若当前锁不可用(已被占有),当前线程会一直休眠直到以下两种情况发生:
 *      1.锁被当前线程获取
 *      2.其他线程中断当前线程,同时锁的获取允许被中断
 * <p><b>Implementation Considerations</b>
 *      实现该方法的注意事项
 * <p>The ability to interrupt a lock acquisition in some
 * implementations may not be possible, and if possible may be an
 * expensive operation.  The programmer should be aware that this
 * may be the case. An implementation should document when this is
 * the case.
 *      该方法属于拓展方法,只有需要中断服务的时候才需要实现它
 * <p>An implementation can favor responding to an interrupt over
 * normal method return.
 *      相对于返回,该方法更适合抛出一个中断响应,比如中断异常
 * @throws InterruptedException if the current thread is
 *         interrupted while acquiring the lock (and interruption
 *         of lock acquisition is supported)
 */
void lockInterruptibly() throws InterruptedException;

4.3 tryLock方法

  • tryLock方法提供可定时与可轮询的锁获取方式,与无条件的锁获取相比,具有更完善的错误恢复机制
  • tryLock方法能够有效的防止死锁的发生,比如使用轮询锁优雅失败规避死锁
  • tryLock方法同时提供定时锁的功能,其允许在限时活动内部使用独占锁,当线程获取锁、被中断或超时后返回
  • tryLock方法支持轮询获取锁:通过一个循环配合tryLock()来不断尝试获取锁,由于tryLock()非阻塞因此会立即返回是否成功获取锁的结果;当不能获取所有的锁时,应释放已获得的所有锁并重新尝试获取
  • tryLock方法同时支持响应中断
/**
 * Acquires the lock only if it is free at the time of invocation.
 *      尝试非阻塞的获取锁,调用该方法后立即返回是否成功获取锁true/false
 * <p>Acquires the lock if it is available and returns immediately
 * with the value {@code true}.
 * If the lock is not available then this method will return
 * immediately with the value {@code false}.
 *      当锁不可用时立即返回false
 * This usage ensures that the lock is unlocked if it was acquired, and
 * doesn't try to unlock if the lock was not acquired.
 *      该实现应确保当锁被获取时是未锁状态,当未被获取时不会尝试解锁
 * @return {@code true} if the lock was acquired and
 *         {@code false} otherwise
 */
boolean tryLock();
/**
 * Acquires the lock if it is free within the given waiting time and the
 * current thread has not been {@linkplain Thread#interrupt interrupted}.
 *      没有被中断当前线程在指定超时时间内获取锁
 * If the lock is not available then the current thread becomes disabled for
 * thread scheduling purposes and lies dormant until one of three things happens:
 * <ul>
 * <li>The lock is acquired by the current thread; or
 * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
 * current thread, and interruption of lock acquisition is supported; or
 * <li>The specified waiting time elapses
 * </ul>
 * <p>If the specified waiting time elapses then the value {@code false} is returned.
 * If the time is less than or equal to zero, the method will not wait at all.
 *      当前线程在以下三种情况下会返回:
 *          1.当前线程在超时时间内获得锁
 *          2.当前线程在超时时间内被中断
 *          3.超时时间结束,返回false,线程不再被阻塞
 * @param time the maximum time to wait for the lock
 * @param unit the time unit of the {@code time} argument
 * @return {@code true} if the lock was acquired and {@code false}
 *         if the waiting time elapsed before the lock was acquired
 * @throws InterruptedException if the current thread is interrupted
 *         while acquiring the lock (and interruption of lock
 *         acquisition is supported)
 */
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;    

4.4 unlock方法

  • 使用lock方法、lockInterruptibly方法、tryLock方法都必须显式调用unlock方法释放锁
  • unlock方法必须在finally块中执行,这也是Lock使用的一个代码隐患(容易忘记执行)
  • unlock方法允许与lock方法不在同一个块(即{})中执行,但业务代码必须保证在try-finally块中执行
/**
 * Releases the lock.
 *  释放锁
 */
void unlock();

4.5 newCondition方法

  • Lock搭配Condition可以实现更加灵活的锁获取与释放的条件控制
/**
 * Returns a new {@link Condition} instance that is bound to this
 * {@code Lock} instance.
 *      返回一个等待通知组(条)件
 * <p>Before waiting on the condition the lock must be held by the current thread.
 * A call to {@link Condition#await()} will atomically release the lock
 * before waiting and re-acquire the lock before the wait returns.
 *      该组件与当前锁绑定,当先线程只有获得锁才能调用该组件的await方法并释放锁
 * @return A new {@link Condition} instance for this {@code Lock} instance
 * @throws UnsupportedOperationException if this {@code Lock}
 *         implementation does not support conditions
 */
Condition newCondition();

参考

《java并发编程的艺术》
并发番@Lock接口一文通(1.8版)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,039评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,426评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,417评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,868评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,892评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,692评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,416评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,326评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,782评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,957评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,102评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,790评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,442评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,996评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,113评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,332评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,044评论 2 355

推荐阅读更多精彩内容

  • 本文是我自己在秋招复习时的读书笔记,整理的知识点,也是为了防止忘记,尊重劳动成果,转载注明出处哦!如果你也喜欢,那...
    波波波先森阅读 11,269评论 4 56
  • Java-Review-Note——4.多线程 标签: JavaStudy PS:本来是分开三篇的,后来想想还是整...
    coder_pig阅读 1,653评论 2 17
  • 在开发Java多线程应用程序中,各个线程之间由于要共享资源,必须用到锁机制。Java提供了多种多线程锁机制的实现方...
    千淘萬漉阅读 6,912评论 1 33
  • 很多时候,我会在突然间做出一些决定,仅仅因为一些微小的感动。 一些熟悉的旋律,一个催泪的广告,一束窗外照进来的阳光...
    是月阅读 193评论 2 2
  • 第一种方式是杜绝一切小说和游戏,但是无聊和乏味仍然普遍,用看b站视频仍然和玩游戏和看小说差不多。 第二种方式是适度...
    柒灬月流火阅读 129评论 0 0