JDK并发包温故知新系列(五)—— 显式锁与显式条件

显式锁-Lock与ReadWriteLock
JDK针对Lock的主要实现是ReentrantLock,ReadWriteLock实现是ReentrantReadWriteLock。本文主要介绍ReentrantLock。

ReentrantReadWriteLock
两把锁共享一个等待队列,两把锁的状态都由一个原子变量表示,特有的获取锁和释放锁逻辑。

ReentrantReadWriteLock的基本原理:
读锁的获取,只要求写锁没有被线程持有就可以获取,检查等待队列,逐个唤醒等待读锁线程,遇到等待写锁线程则停止.
读锁的释放,释放后,检查写锁和读锁是否被持有,若都没有被持有则唤醒下一个等待线程.
写锁的获取,只有读写锁都未被持有才会获取写锁。
写锁的释放,唤醒等待队列的下一个线程。
ReentrantLock
主要方法
void lock();获取锁,阻塞,不响应中断,但会记录中断标志位。
void lockInterruptibly() throws InterruptedException;获取锁,响应中断
boolean tryLock();获取锁,不阻塞,实时返回,一般需循环调用
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;在time的时间内阻塞获取锁,响应中断
void unlock();释放锁
Condition newCondition();新建显式条件
注: 这里的响应中断意思是若被其他线程中断(调用interrupt方法)会抛出InterruptedException异常。

原理支持
依赖CAS方法,可重入实现用的计数就是用的原子变量。
依赖LockSupport中的方法:
public static void park():放弃CPU执行权,CPU不在进行调度,响应中断,当有中断发生时,park会返回,线程中断状态会被设置,另外park也有可能无缘无故的返回,所以一般需要循环检查park的等待条件是否满足。。
public static void parkNanos(long nanos):在nanos纳秒内放弃CPU执行权
public static void parkUntil(long deadline):放弃执行权直到deadline时间(距离1970年毫秒数)。
public static void unpark(Thread thread):重新恢复线程,让其争夺CPU执行权。
实现基础AQS
AQS-AbstractQueuedSynchronizer(抽象队列同步器)。

ReadWriteLock在内部注入了AbstractQueuedSynchronizer,上锁和释放锁核心方法都在AQS类当中,AQS维护了两个核心变量,一个是state(当前可重入计数,初始值为0),一个是exclusiveOwnerThread(当前持有锁的线程Thread对象)。另外还维护了一个锁等待队列。

ReentrantLock构造方法传入的boolean值ture为公平锁,false为不公平锁。以不公平锁为例先讲一下上锁和释放锁的原理:

上锁
如果当前锁状态为0(未被锁),则使用CAS获得锁,并设置当前锁内的线程为自己。
如果不为0,且持有锁的线程不是自己,则添加到队列尾部,并调用LockSupport中的park()方法放弃CPU执行权。直到当锁被释放的时候被唤醒,被唤醒后检查自己是否是第一个等待的线程,如果是且能获得锁,则返回,否则继续等待,这个过程中如果发生了中断,lock会记录中断标志位,但不会提前返回或抛出异常。
如果不为0,但持有锁线程是自己,则直接将state加1。
释放锁
就是将AQS内的state变量的值递减1,如果state值为0,则彻底释放锁,会将“加锁线程”变量也设置为null,同时唤醒等待队列中的第一个线程。

公平锁
为什么说上面的是不公平锁,释放锁时不是唤醒队列中第一个线程吗?为什么还会出现不公平的情况了,原因在于如果刚好释放锁,此时有一个线程进来尝试获取锁,可能会存在插队的情况。

公平锁原理
构造方法bollean传入true则代表的是公平锁,在获取锁方法中多了一个检查,意义是只有不存在其他等待时间更长的线程,它才会尝试获取锁。对比不公平锁,其整体性能比较低,低的原因不是这个检查慢,而是会让活跃线程得不到锁,进入等待状态,引起上下文切换,降低了整体的效率,

与synchrnized的区别
tryLock可避免死锁造成的无限等待
拥有获取锁信息方法的各种API
可以响应中断
可以限时
建议: synchronized以前的效率不如显式锁,但现在的版本两者效率上几乎没有区别,所以建议能用synchronized就用synchronized,需要实现synchronized办不到的需求如以上区别时,再考虑ReentrantLock。

显示条件
什么是显示条件
与wait和notify对应,用于线程协作,通过Lock的Condition newCondition()方法创建对应显示锁的显示条件;

方法
主要方法是await()和signal(),await()对应于Object的wait(),signal()对应于notify,signalAll()对应于notifyAll()

用法示例
public class WaitThread extends Thread {
private volatile boolean fire = false;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();

@Override
public void run() {
try {
lock.lock();
try {
while (!fire) {
condition.await();
}
} finally {
lock.unlock();
}
System.out.println("fired");
} catch (InterruptedException e) {
Thread.interrupted();
}
}

public void fire() {
lock.lock();
try {
this.fire = true;
condition.signal();
} finally {
lock.unlock(显式锁-Lock与ReadWriteLock
JDK针对Lock的主要实现是ReentrantLock,ReadWriteLock实现是ReentrantReadWriteLock。本文主要介绍ReentrantLock。

ReentrantReadWriteLock
两把锁共享一个等待队列,两把锁的状态都由一个原子变量表示,特有的获取锁和释放锁逻辑。

ReentrantReadWriteLock的基本原理:
读锁的获取,只要求写锁没有被线程持有就可以获取,检查等待队列,逐个唤醒等待读锁线程,遇到等待写锁线程则停止.
读锁的释放,释放后,检查写锁和读锁是否被持有,若都没有被持有则唤醒下一个等待线程.
写锁的获取,只有读写锁都未被持有才会获取写锁。
写锁的释放,唤醒等待队列的下一个线程。
ReentrantLock
主要方法
void lock();获取锁,阻塞,不响应中断,但会记录中断标志位。
void lockInterruptibly() throws InterruptedException;获取锁,响应中断
boolean tryLock();获取锁,不阻塞,实时返回,一般需循环调用
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;在time的时间内阻塞获取锁,响应中断
void unlock();释放锁
Condition newCondition();新建显式条件
注: 这里的响应中断意思是若被其他线程中断(调用interrupt方法)会抛出InterruptedException异常。

原理支持
依赖CAS方法,可重入实现用的计数就是用的原子变量。
依赖LockSupport中的方法:
public static void park():放弃CPU执行权,CPU不在进行调度,响应中断,当有中断发生时,park会返回,线程中断状态会被设置,另外park也有可能无缘无故的返回,所以一般需要循环检查park的等待条件是否满足。。
public static void parkNanos(long nanos):在nanos纳秒内放弃CPU执行权
public static void parkUntil(long deadline):放弃执行权直到deadline时间(距离1970年毫秒数)。
public static void unpark(Thread thread):重新恢复线程,让其争夺CPU执行权。
实现基础AQS
AQS-AbstractQueuedSynchronizer(抽象队列同步器)。

ReadWriteLock在内部注入了AbstractQueuedSynchronizer,上锁和释放锁核心方法都在AQS类当中,AQS维护了两个核心变量,一个是state(当前可重入计数,初始值为0),一个是exclusiveOwnerThread(当前持有锁的线程Thread对象)。另外还维护了一个锁等待队列。

ReentrantLock构造方法传入的boolean值ture为公平锁,false为不公平锁。以不公平锁为例先讲一下上锁和释放锁的原理:

上锁
如果当前锁状态为0(未被锁),则使用CAS获得锁,并设置当前锁内的线程为自己。
如果不为0,且持有锁的线程不是自己,则添加到队列尾部,并调用LockSupport中的park()方法放弃CPU执行权。直到当锁被释放的时候被唤醒,被唤醒后检查自己是否是第一个等待的线程,如果是且能获得锁,则返回,否则继续等待,这个过程中如果发生了中断,lock会记录中断标志位,但不会提前返回或抛出异常。
如果不为0,但持有锁线程是自己,则直接将state加1。
释放锁
就是将AQS内的state变量的值递减1,如果state值为0,则彻底释放锁,会将“加锁线程”变量也设置为null,同时唤醒等待队列中的第一个线程。

公平锁
为什么说上面的是不公平锁,释放锁时不是唤醒队列中第一个线程吗?为什么还会出现不公平的情况了,原因在于如果刚好释放锁,此时有一个线程进来尝试获取锁,可能会存在插队的情况。

公平锁原理
构造方法bollean传入true则代表的是公平锁,在获取锁方法中多了一个检查,意义是只有不存在其他等待时间更长的线程,它才会尝试获取锁。对比不公平锁,其整体性能比较低,低的原因不是这个检查慢,而是会让活跃线程得不到锁,进入等待状态,引起上下文切换,降低了整体的效率,

与synchrnized的区别
tryLock可避免死锁造成的无限等待
拥有获取锁信息方法的各种API
可以响应中断
可以限时
建议: synchronized以前的效率不如显式锁,但现在的版本两者效率上几乎没有区别,所以建议能用synchronized就用synchronized,需要实现synchronized办不到的需求如以上区别时,再考虑ReentrantLock。

显示条件
什么是显示条件
与wait和notify对应,用于线程协作,通过Lock的Condition newCondition()方法创建对应显示锁的显示条件;

方法
主要方法是await()和signal(),await()对应于Object的wait(),signal()对应于notify,signalAll()对应于notifyAll()

用法示例
public class WaitThread extends Thread {
private volatile boolean fire = false;
private Lock lock = new ReentrantLock(www.yuanyangyuL.com);
private Condition condition = lock.newCondition();

@Override
public void run() {
try {
lock.lock();
try {
while (!fire) {
condition.await();
}
} finally {
lock.unlock();
}
System.out.println("fired");
} catch (InterruptedException e) {
Thread.interrupted(www.osgjyl.com );
}
}

public void fire() {
lock.lock();
try {
this.fire = true;
condition.signal();
} finally {
lock.unlock();
}
}

public static void main(String[] args) throws InterruptedException {
WaitThread waitThread = new WaitThread();
waitThread.start(www.qjljdgt.cn);
Thread.sleep(1000);
System.out.println("fire");
waitThread.fire(www.xinhuihpw.com);
}
}
当主线程调用fire方法时,子线程才被唤醒继续执行。);
}
}

public static void main(String[] args) throws InterruptedException {
WaitThread waitThread = new WaitThread();
waitThread.start(www.rhyl158.com;
Thread.sleep(1000);
System.out.println("fire");
waitThread.fire();
}
}
当主线程调用fire方法时,子线程才被唤醒继续执行。

原文地址:https://www.cnblogs.com/dakunqq/p/11621322.html

时间: 2024-11-13 20:17:23

JDK并发包温故知新系列(五)—— 显式锁与显式条件的相关文章

从零开始搭建django前后端分离项目 系列五(实战之excel流式导出)

项目中有一处功能需求是:需要在历史数据查询页面进行查询字段的选择,然后由后台数据库动态生成对应的excel表格并下载到本地. 如果文件较小,解决办法是先将要传送的内容全生成在内存中,然后再一次性传入Response对象中: 如果文件较大时,我们可以考虑向HttpResponse传递一个迭代器,流式的向客户端传递数据. view.py视图 @csrf_exempt def exportData(request): format = request.GET.get('format') pk = re

innodB的隐式锁

http://blog.csdn.net/taozhi20084525/article/details/19545231 一.知识准备之隐式锁 参考:http://www.uml.org.cn/sjjm/201205302.asp Innodb 实现了一个延迟加锁的机制,来减少加锁的数量,在代码中称为隐式锁(Implicit Lock).隐式锁中有个重要的元素,事务ID(trx_id).隐式锁的逻辑过程如下: A. InnoDB的每条记录中都一个隐含的trx_id字段,这个字段存在于簇索引的B+

隐式锁

Lock 是一种悲观的顺序化机制.它假设很可能发生冲突,因此在操作数据时,就加锁.如果冲突的可能性很小,多数的锁都是不必要的. Innodb 实现了一个延迟加锁的机制,来减少加锁的数量,在代码中称为隐式锁(Implicit Lock).隐式锁中有个重要的元素,事务ID(trx_id). 隐式锁的逻辑过程如下:A. InnoDB的每条记录中都一个隐含的trx_id字段,这个字段存在于簇索引的B+Tree中.B. 在操作一条记录前,首先根据记录中的trx_id检查该事务是否是活动的事务(未提交或回滚

【C++自我精讲】基础系列五 隐式转换和显示转换

0 前言 1)C++的类型转换分为两种,一种为隐式转换,另一种为显式转换. 2)C++中应该尽量不要使用转换,尽量使用显式转换来代替隐式转换. 1 隐式转换 定义:隐式转换是系统跟据程序的需要而自动转换的. 1)C++类型(char,int,float,long,double等)的隐式转换: 算术表达式隐式转换顺序为: 1.char - int - long - double 2.float - double //1)算术表达式 int m = 10; double n = m;//n = 10

Java并发编程系列-(4) 显式锁与AQS

4 显示锁和AQS 4.1 Lock接口 核心方法 Java在java.util.concurrent.locks包中提供了一系列的显示锁类,其中最基础的就是Lock接口,该接口提供了几个常见的锁相关的操作. public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit

并发编程—4显式锁 Lock

目录 4.显式锁 Lock 4.1 概念 内置锁 vs 显示锁 可重入锁 vs 不可重入锁 公平锁 vs 非公平锁 读锁 vs 写锁 4.2 ReentrantLock源码解读 4.显式锁 Lock 4.1 概念 内置锁 vs 显示锁 synchronize是java语言层面实现的锁,称为内置锁.使用方便代码简洁,而且在jdk新版本优化后,性能也得到了很大的提高.synchronize是一个可重入锁.而Lock是jdk提供开发者是用的一个显式锁.通过lock()和unlock()方法加锁和释放锁

1.扩展方法2.接口的隐式实现和显式实现

1.扩展方法:必须写在一个静态类里面,具体见代码: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 namespace ConsoleApplication1 {     class Program     {         static void Main(string[] args)         {             Student s = new Stud

Apache Kafka系列(五) Kafka Connect及FileConnector示例

Apache Kafka系列(一) 起步 Apache Kafka系列(二) 命令行工具(CLI) Apache Kafka系列(三) Java API使用 Apache Kafka系列(四) 多线程Consumer方案 Apache Kafka系列(五) Kafka Connect及FileConnector示例 一. Kafka Connect简介 Kafka是一个使用越来越广的消息系统,尤其是在大数据开发中(实时数据处理和分析).为何集成其他系统和解耦应用,经常使用Producer来发送消

第13章 显式锁

性能是一个不断变化的指标,如果在昨天的测试基准中发现X比Y更快,那么在今天就可能已经过时了. 在激烈竞争的情况下,在非公平锁的性能高于公平锁的性能的一个原因是:在恢复一个被挂起的线程与该线程真正开始运行之间存在着严重的延迟.假设线程A持有一个锁,并且线程B请求这个锁.由于这个锁已被线程A持有,因此B将被挂起.当A释放锁时,B将被唤醒,因此会再次尝试获取锁.与此同时,如果C也请求这个锁,那么C很可能会在B被完全唤醒之前获得.使用以及释放这个锁.这样的情况是一种“双赢”的局面:B获得锁的时刻并没有推