前面的介绍中,对于显式锁的概念进行了简单介绍
显式锁的概念,是基于JDK层面的实现,是接口,通过这个接口可以实现同步访问
而不同于synchronized关键字,他是Java的内置特性,是基于JVM的实现
Lock接口的核心概念很简单,只有如下几个方法
按照逻辑可以进行如下划分

lock()

Lock接口,所以synchronized关键字更为灵活的一种同步方案,在实际使用中,自然是能够替代synchronized关键字的
(ps:尽管你不需要总是使用显式锁,显式锁与隐式锁各有利弊,但是在语法上是的确可以替代的)
synchronized关键字是阻塞式的获取锁
lock方法就是这一逻辑的体现,也就是说对于lock()方法,如果获取不到锁,那么将会进入阻塞状态,与synchronized关键字一样

lockInterruptibly()

Lock()方法是一种阻塞式的,另外Lock接口还提供了可中断的lock获取方法,先看下测试例子
package test2;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class T28 {
private static final Lock LOCK = new ReentrantLock();
public static void main(String[] args) {
//线程A获取加锁之后,持有五秒钟
Thread threadA = new Thread(() -> {
LOCK.lock();
try {
System.out.println(Thread.currentThread().getName() + " " + System.currentTimeMillis());
System.out.println(Thread.currentThread().getName() + " sleep");
TimeUnit.SECONDS.sleep(10);
System.out.println(Thread.currentThread().getName() + " " + System.currentTimeMillis());
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " interrupt");
} finally {
LOCK.unlock();
}
}, "thread-A");
threadA.start();
//线程B开始后,尝试获取锁
Thread threadB = new Thread(() -> {
LOCK.lock();
try {
System.out.println(Thread.currentThread().getName() + " " + System.currentTimeMillis());
System.out.println(Thread.currentThread().getName() + " working");
} finally {
LOCK.unlock();
}
}, "thread-B");
threadB.start();
//为了确保上面的任务都开始了,主线程sleep 1s
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
threadB.interrupt();
}
}
示例逻辑
两个线程A和B,使用同一把锁
A线程获取锁后,休眠10s,紧接着B尝试获取锁
为了保证前面的任务都开始了,主线程sleep 1s后,将线程B进行中断
对于lock方法,如同synchronized关键字,是阻塞式的,通过执行来看,可以发现,在A持有锁期间,线程B也是一直阻塞的,是不能够获取到锁,也不能被中断(上面示例中调用interrupt()没有任何的反应)
将代码稍作修改,也就是将lock方法修改为lockInterruptibly()方法,其他暂时不变
再次运行,你会发现马上就被中断了,而不是傻傻的等待A结束
当然,因为根本都没有获取到锁,所以在finally中尝试unlock时,将会抛出异常,这个暂时不管了,通过这个例子可以看得出来
对于lockInterruptibly方法,这是一个“可中断的锁获取操作”
小结
lockInterruptibly就是一个可中断的锁获取操作,在尝试获取锁的过程中,如果不能够获取到,如果被中断,那么它将能够感知到这个中断,而不是一直阻塞下去
如果锁不可用(被其他线程持有),除非发生以下事件,否则将会等待
  • 该线程成功获得锁
  • 发生中断
如果当前线程遇到下面的事件,则将抛出 InterruptedException,并清除当前线程的已中断状态。
  • 在进入此方法时已经设置了该线程的中断状态
  • 在获取锁时被中断
从上面的分析可以看得出来,如果什么都没发生,这个方法与lock方法并没有什么区别,就是在等待获取锁,获取不到将会阻塞
他只是额外的对可中断提供了支持  

unlock()

unlock并没有什么特殊的,他替代了synchronized关键字隐式的解锁操作
通常需要在finally中确保unlock操作会被执行,之前提到过,对于synchronized关键字解锁是隐式的,也是必然的,即使出现错误,JVM也会保障能够正确的解锁
但是对于Lock接口提供的unlock操作,则必须自己确保能够正确的解锁  

tryLock()

相对于synchronized,Lock接口另一大改进就是try lock
顾名思义,尝试获取锁,既然是尝试,那显然并不会势在必得
tryLock方法就是一次尝试,如果锁可用,则获取锁,并立即返回值 true。如果锁不可用,则此方法将立即返回值 false
也就是说方法会立即返回,如果获取到锁返回true,否则返回false,不管如何都是立马返回
典型的用法就是如下所示,下面的代码还能够确保如果没有获取锁,不会试图进行unlock操作
Lock lock = ...;
if (lock.tryLock()) {
try {
// manipulate protected state
} finally {
lock.unlock();
}
} else {
// perform alternative actions
}
tryLock只是一次尝试,如果你需要不断地进行尝试,那么可以使用while替代if的条件判断
尽管tryLock只是一次的测试,但是可以借助于循环(有限或者无限)进行多次测试  

tryLock(long time, TimeUnit unit)

对于TryLock还有可中断、配置超时时间的版本
boolean tryLock(long time,
                TimeUnit unit)
                throws InterruptedException
两个参数,第一个为值,第二个为第一个参数的单位,比如1,单位秒,或者2 ,单位分钟
在指定的超时时间内,如果能够获取到锁,那么将会返回true;
如果超过了指定的时间,但是却不能获取到锁,那么将会返回false;
另外很显然,这个方法是可中断的,也就是说如果尝试过程中,出现了中断,那么他将会抛出InterruptedException
所以,对于这个方法,他会一直尝试获取锁(也可以认为是一定时长内的“阻塞”,当然可以被中断),除非:
  • 该线程成功获得锁
  • 超过了超时时长
  • 该线程被中断
可以认为是lockInterruptibly的限时版本
如果没有发生中断,也认为他就是“定时版本的lock()”
不管怎么理解,只需要记住:他会在一定时长内尝试进行锁的获取,也支持中断

锁小结

对于lock方法和unlock方法,就是类似于synchronized关键字的加锁和解锁,并没有什么特别的
其他几个方法是Lock接口针对于锁获取的阻塞以及可中断两个方面进行了拓展
隐式锁的阻塞以及不可中断,导致一旦开始尝试获取,那么则没办法唤醒,将会一直等待,除非获得
  • lockInterruptibly()是阻塞式的,如果获取不到会一直等待,但是他是可中断的,能够通过阻塞打破这种等待
  • tryLock()不会进行任何阻塞,只是尝试获取一下,能获取到就获取,获取不到就false,拉倒
  • tryLock(long time, TimeUnit unit),即是可中断的,又是限时阻塞的,即使不中断,也不会一直阻塞,即使处于阻塞中(超时时长还没到),也可以随时中断
对于lockInterruptibly()方法以及tryLock(long time, TimeUnit unit),都支持中断,但是需要注意:
在某些实现中可能无法中断锁获取,即使可能,该操作的开销也很大  

Condition

在隐式锁的逻辑中,借助于Java底层机制,每个对象都有一个相关联的锁与监视器
对于synchronized的隐式锁逻辑就是借助于锁与监视器,从而进行线程的同步与通信协作
在显式锁中,Lock接口提供了synchronized的语意,对于监视器的概念,则借助于Condition,但是很显然,Condition也是与锁关联的
Lock接口提供了方法Condition newCondition();
Condition也是一个接口,他定义了相关的监视器方法
在显式锁中,可以定义多个Condition,也就是一个锁,可以对应多个监视器,可以更加细粒度的进行同步协作的处理

总结

Lock接口提供了相对于synchronized关键字,而更为灵活的一种同步手段
它的核心与本质仍旧是为了线程的同步与协作通信
所以它的核心仍旧是锁与监视器,也就是Lock接口与Condition接口
但是灵活是有代价的,所以并不需要在所有的地方都尝试使用显式锁,如果场景满足需要,synchronized仍旧是一种很好的解决方案(也是应该被优先考虑的一种方式)
与synchronized再次对比下
  • synchronized是JVM底层实现的,Lock是JDK接口层面的
  • synchronized是隐式的,Lock是显式的,需要手动加锁与解锁
  • synchronized乌无论如何都会释放,即使出现错误,Lock需要自己保障正确释放
  • synchronized是阻塞式的获取锁,Lock可以阻塞获取,可中断,还可以尝试获取,还可以设置超时等待获取
  • synchronized无法判断锁的状态,Lock可以进行判断
  • synchronized可重入,不可中断,非公平,Lock可重入,可中断、可配置公平性(公平和非公平都可以)
  • 如果竞争不激烈,两者的性能是差不多的,可是synchronized的性能还在不断的优化,当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized
  • 等
对于Lock接口,他仍旧是一个对象,所以他是否可以用来作为锁以及调用监视器方法(用在synchronized(lock)中)?
这逻辑上是没问题的,但是最好不要那么做,因为很容易引起混淆的,不管是维护上还是易读性上都有很大的问题
在lock上调用他的监视器方法,与借助于lock实现线程的同步,本质上是没有什么关系的
尽管看起来Lock是那么的优秀,但是还是要再次提醒,除非synchronized真的不行,否则你应该使用synchronized而不是Lock
 

java多线程Lock接口简介使用与synchronized对比 多线程下篇(三)的更多相关文章

  1. Lock接口简介

    在Java多线程编程中,我们经常使用synchronized关键字来实现同步,控制多线程对变量的访问,来避免并发问题. 但是有的时候,synchronized关键字会显得过于沉重,不够灵活.synch ...

  2. 【Java基础】接口和抽象类之间的对比

    Java 中的接口和抽象类之间的对比 一.接口 Interface,将其翻译成插座可能就更好理解了.我们通常利用接口来定义实现类的行为,当你将插座上连接笔记本的三角插头拔掉,换成微波炉插上去的时候,你 ...

  3. Java基础知识强化之多线程笔记06:Lock接口 (区别于Synchronized块)

    1. 简介 我们讲到了如何使用关键字synchronized来实现同步访问.本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式 ...

  4. jdk1.5多线程Lock接口及Condition接口

    jdk1.5多线程的实现的方式: jdk1.5之前对锁的操作是隐式的 synchronized(对象) //获取锁 { } //释放锁 jdk1.5锁的操作是显示的:在包java.util.concu ...

  5. java 锁 Lock接口详解

    一:java.util.concurrent.locks包下常用的类与接口(lock是jdk 1.5后新增的) (1)Lock和ReadWriteLock是两大锁的根接口,Lock代表实现类是Reen ...

  6. Java并发Lock接口

    java.util.concurrent.locks.Lock接口用作线程同步机制,类似于同步块.新的锁定机制更灵活,提供比同步块更多的选项. 锁和同步块之间的主要区别如下: 序列的保证 - 同步块不 ...

  7. Java多线程(五) Lock接口,ReentranctLock,ReentrantReadWriteLock

    在JDK5里面,提供了一个Lock接口.该接口通过底层框架的形式为设计更面向对象.可更加细粒度控制线程代码.更灵活控制线程通信提供了基础.实现Lock接口且使用得比较多的是可重入锁(Reentrant ...

  8. Java多线程的~~~Lock接口和ReentrantLock使用

    在多线程开发.除了synchronized这个keyword外,我们还通过Lock接口来实现这样的效果.由Lock接口来实现 这样的多线程加锁效果的优点是非常的灵活,我们不在须要对整个函数加锁,并且能 ...

  9. 多线程里面的关键字,wait, notfiy, 锁(synchronized), lock接口

    多线程环境下,必须考虑线程同步的问题,这是因为多个线程同时访问变量或者资源时会有线程争用,比如A线程读取了一个变量,B线程也读取了这个变量,然后他们同时对这个变量做了修改,写回到内存中,由于是同时做修 ...

随机推荐

  1. RocketMQ源码 — 十一、 RocketMQ事务消息

    分布式事务是一个复杂的问题,rmq实现了事务的最终一致性,rmq保证本地事务成功消息一定会发送成功并被成功消费,如果本地事务失败了,消息不会被发送. rmq事务消息的实现过程为: producer发送 ...

  2. visual studio2015中开发python

    之前下载了visual studio2017但是发现很不好用,不如使用matlab与visual studio混合编程就根本找不到visual studio,只有下了visual studio2015 ...

  3. 关于throw、throws、try--catch的问题

    首先回顾概念 throws表示出现异常的一种可能性,并不一定会发生这些异常 throw则是抛出了异常,执行throw则一定抛出了某种异常 try--catch try语句用大括号{}指定了一段代码,该 ...

  4. Vue-CLI和脚手架

    但我们学习Vue时,很多教程都会说到用Vue-CLI构建项目,那么什么是脚手架?什么是Vue-CLI?为什么要用脚手架,好处在哪?以及为何我们用Vue开发项目时要用到Vue-CLI? 首先,CLI为c ...

  5. Java的多态浅谈

    概述 Java的四大基本特性:抽象,封装,继承和多态.其中,抽象,封装,继承可以说多态的基础,而多态是封装,继承的具体表现.如果非要用专业术语来描述什么是多态的话 多态是指程序中定义的引用变量所指向具 ...

  6. ORM 开发环境之利器:MVC 中间件 FreeSql.AdminLTE

    前言 这是一篇纯技术干货的分享文章,FreeSql 已经基本完成 .NETCore 最方便的 ORM 使命,我们正在筹备生态的建立,比如 ABP 中如何使用 FreeSql 的实现,需要各种各样的扩展 ...

  7. 目标检测之R-CNN系列

    Object Detection,在给定的图像中,找到目标图像的位置,并标注出来. 或者是,图像中有那些目标,目标的位置在那.这个目标,是限定在数据集中包含的目标种类,比如数据集中有两种目标:狗,猫. ...

  8. C# 操作Word页眉页脚——奇偶页/首页不同、不连续设置页码、复制页眉页脚、锁定页眉页脚、删除页眉页脚

    前言 本文是对Word页眉页脚的操作方法的进一步的阐述.在“C# 添加Word页眉页脚.页码”一文中,介绍了添加简单页眉页脚的方法,该文中的方法可满足于大多数的页眉页脚添加要求,但是对于比较复杂一点的 ...

  9. Java虚拟机三:OutOfMemoryError异常分析

    根据Java虚拟机规范,虚拟机内存中除过程序计数器之外的运行时数据区域都会发生OutOfMemoryError(OOM),本文将通过实际例子验证分析各个数据区域OOM的情况.为了更贴近生产,本次所有例 ...

  10. 【Matlab&Mathematica】对三维空间上的点进行椭圆拟合

    问题是这样:比如有一个地心惯性系的轨道,然后从轨道上取了几个点,问能不能根据这几个点把轨道还原了? 当然,如果知道轨道这几个点的速度的情况下,根据轨道六根数也是能计算轨道的,不过真近点角是随时间变动的 ...