Java显式锁
Java 显式锁。
一、显式锁
什么是显式锁?
由自己手动获取锁,然后手动释放的锁。
有了 synchronized(内置锁) 为什么还要 Lock(显示锁)?
使用 synchronized 关键字实现了锁功能的,使用 synchronized 关键字将会隐式地获取锁,但是它将锁的获取和释放固化了,也就是先获取再释放。
与内置加锁机制不同的是,Lock 提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有加锁和解锁的方法都是显式的。
二、Lock的常用api
方法名称 | 描述 |
---|---|
void lock() | 获取锁 |
void lockInterruptibly() throws InterruptedException | 可中断的获取锁,和lock()方法的不同之处在于该方法会响应中断,即在锁的获取中可以中断当前线程 |
boolean tryLock() | 尝试非阻塞的获取锁,调用该方法后立刻返回,如果能够获取则返回true,否则返回false |
boolean tryLock(long time, TimeUnit unit) throws InterruptedException | 超时获取锁,当前线程会在以下三种情况下会返回: 1. 当前线程在超时时间内获得了锁 2.当前线程在超市时间内被中断 3. 超时时间结束,返回false |
void unlock(); | 释放锁 |
三、Lock的标准用法
lock.lock();
try {
// 业务逻辑
} finally {
lock.unlock();
}
- 在 finally 块中释放锁,目的是保证在获取到锁之后,最终能够被释放。
- 不要将获取锁的过程写在 try 块中,因为如果在获取锁(自定义锁的实现)时发生了异常,异常抛出的同时,也会导致锁无故释放。
四、ReentrantLock(可重入锁)
Lock接口常用的实现类是 ReentrantLock。
示例代码:主线程100000次减,子线程10万次加。
public class ReentrantLockTest {
private Lock lock = new ReentrantLock();
private int count = 0;
public int getCount() {
return count;
}
private static class ReentrantLockThread extends Thread {
private ReentrantLockTest reentrantLockTest;
public ReentrantLockThread(ReentrantLockTest reentrantLockTest) {
this.reentrantLockTest = reentrantLockTest;
}
@Override
public void run() {
for (int i = 0; i < 100000; i++) {
reentrantLockTest.incr();
}
System.out.println(Thread.currentThread().getName() + " end, count = " + reentrantLockTest.getCount());
}
}
private void incr() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
private void decr() {
lock.lock();
try {
count--;
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ReentrantLockTest reentrantLockTest = new ReentrantLockTest();
new ReentrantLockThread(reentrantLockTest).start();
for (int i = 0; i < 100000; i++) {
// 递减100000
reentrantLockTest.decr();
}
System.out.println(Thread.currentThread().getName() + " count = " + reentrantLockTest.getCount());
}
}
1. 锁的可重入性
简单地讲就是:“同一个线程对于已经获得到的锁,可以多次继续申请到该锁的使用权”。而 synchronized 关键字隐式的支持重进入,比如一个 synchronized 修饰的递归方法,在方法执行时,执行线程在获取了锁之后仍能连续多次地获得该锁
同样,ReentrantLock 在调用 lock()方法时,已经获取到锁的线程,能够再次调用lock()方法获取锁而不被阻塞
2. 公平锁和非公平锁
- 如果在时间上,先对锁进行获取的请求一定先被满足,那么这个锁是公平的,反之,是不公平的。公平的获取锁,也就是等待时间最长的线程最优先获取锁,也可以说锁获取是顺序的。
- ReentrantLock 提供了一个构造函数,能够控制锁是否是公平的(缺省为不公平锁)。事实上,公平的锁机制往往没有非公平的效率高。
- 在激烈竞争的情况下,非公平锁的性能高于公平锁的性能的一个原因是:在恢复一个被挂起的线程与该线程真正开始运行之间存在着严重的延迟。
- 假设线程 A 持有一个锁,并且线程 B 请求这个锁,由于这个锁已被线程 A 持有,因此 B 将被挂起。当 A 释放锁时,B 将被唤醒,因此会再次尝试获取锁。与此同时,如果 C 也请求这个锁,那么 C 很可能会在 B 被完全唤醒之前获得、使用以及释放这个锁,这样的情况是一种“双赢”的局面:B 获得锁的时刻并没有推迟,C 更早地获得了锁,完成了自己的任务,然后释放了锁,并且吞吐量也获得了提高。
五、ReentrantReadWriteLock(读写锁)
ReentrantReadWriteLock 是 ReadWriteLock 的实现类。
之前提到锁基本都是排他锁,这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时刻可以允许多个读线程访问但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大提升。
读锁不排斥读锁,但是排斥写锁;写锁即排斥读锁也排斥写锁。
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock getLock = lock.readLock(); // 读锁
private final Lock setLock = lock.writeLock(); // 写锁
至于上锁、解锁与 ReentrantLock 使用方式一致。
六、Condition
- 任意一个 Java 对象,都拥有一组监视器方法(定义在 java.lang.Object 上),主要包括 wait()、wait(long timeout)、notify()以及 notifyAll()方法,这些方法与 synchronized 同步关键字配合,可以实现等待/通知模式。
- Condition 接口也提供了类似 Object 的监视器方法,与 Lock 配合可以实现等待/通知模式。
常用api
方法名称 | 描述 |
---|---|
void await() throws InterruptedException | 使当前线程进入等待状态直到被通知(signal)或中断 |
void signal() | 唤醒一个等待的线程 |
void signalAll() | 唤醒所有等待的线程 |
示例代码,主线程调用方法唤醒两个子线程。
public class ConditionTest {
private volatile boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private void task1() {
lock.lock();
try {
try {
System.out.println(Thread.currentThread().getName() + " 等待中");
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 等待结束");
System.out.println("发送邮件");
} finally {
lock.unlock();
}
}
private void task2() {
lock.lock();
try {
try {
System.out.println(Thread.currentThread().getName() + " 等待中");
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 等待结束");
System.out.println("发送短信");
} finally {
lock.unlock();
}
}
private void updateFlag() {
lock.lock();
try {
this.flag = true;
this.condition.signalAll();
}finally {
lock.unlock();
}
}
private static class ConditionThread1 extends Thread {
private ConditionTest conditionTest;
public ConditionThread1(ConditionTest conditionTest) {
this.conditionTest = conditionTest;
}
@Override
public void run() {
if (!conditionTest.flag) {
conditionTest.task1();
}
}
}
private static class ConditionThread2 extends Thread {
private ConditionTest conditionTest;
public ConditionThread2(ConditionTest conditionTest) {
this.conditionTest = conditionTest;
}
@Override
public void run() {
if (!conditionTest.flag) {
conditionTest.task2();
}
}
}
public static void main(String[] args) throws InterruptedException {
ConditionTest conditionTest = new ConditionTest();
new ConditionThread1(conditionTest).start();
new ConditionThread2(conditionTest).start();
Thread.sleep(1000);
System.out.println("flag 改变。。。");
conditionTest.updateFlag();
}
}
都读到这里了,来个 点赞、评论、关注、收藏 吧!
文章作者:IT王小二
首发地址:https://www.itwxe.com/posts/4e06845c/
版权声明:文章内容遵循 署名-非商业性使用-禁止演绎 4.0 国际 进行许可,转载请在文章页面明显位置给出作者与原文链接。
Java显式锁的更多相关文章
- Java显式锁学习总结之二:使用AbstractQueuedSynchronizer构建同步组件
Jdk1.5中包含了并发大神Doug Lea写的并发工具包java.util.concurrent,这个工具包中包含了显示锁和其他的实用同步组件.Doug Lea在构建锁和组件的时候,大多是以队列同步 ...
- Java显式锁学习总结之六:Condition源码分析
概述 先来回顾一下java中的等待/通知机制 我们有时会遇到这样的场景:线程A执行到某个点的时候,因为某个条件condition不满足,需要线程A暂停:等到线程B修改了条件condition,使con ...
- Java显式锁学习总结之五:ReentrantReadWriteLock源码分析
概述 我们在介绍AbstractQueuedSynchronizer的时候介绍过,AQS支持独占式同步状态获取/释放.共享式同步状态获取/释放两种模式,对应的典型应用分别是ReentrantLock和 ...
- Java显式锁学习总结之四:ReentrantLock源码分析
概述 ReentrantLock,即重入锁,是一个和synchronized关键字等价的,支持线程重入的互斥锁.只是在synchronized已有功能基础上添加了一些扩展功能. 除了支持可中断获取锁. ...
- Java显式锁学习总结之三:AbstractQueuedSynchronizer的实现原理
概述 上一篇我们讲了AQS的使用,这一篇讲AQS的内部实现原理. 我们前面介绍了,AQS使用一个int变量state表示同步状态,使用一个隐式的FIFO同步队列(隐式队列就是并没有声明这样一个队列,只 ...
- Java显式锁学习总结之一:概论
我们都知道在java中,当多个线程需要并发访问共享资源时需要使用同步,我们经常使用的同步方式就是synchronized关键字,事实上,在jdk1.5之前,只有synchronized一种同步方式.而 ...
- Java并发编程之显式锁机制
我们之前介绍过synchronized关键字实现程序的原子性操作,它的内部也是一种加锁和解锁机制,是一种声明式的编程方式,我们只需要对方法或者代码块进行声明,Java内部帮我们在调用方法之前和结束时加 ...
- 深入理解Java内置锁和显式锁
synchronized and Reentrantlock 多线程编程中,当代码需要同步时我们会用到锁.Java为我们提供了内置锁(synchronized)和显式锁(ReentrantLock)两 ...
- java并发多线程显式锁Condition条件简介分析与监视器 多线程下篇(四)
Lock接口提供了方法Condition newCondition();用于获取对应锁的条件,可以在这个条件对象上调用监视器方法 可以理解为,原本借助于synchronized关键字以及锁对象,配备了 ...
随机推荐
- 【mybatis】mybaits generator 逆向工程的使用
mybatis逆向工程官方网站:http://www.mybatis.org/generator/quickstart.html 准备xml文件.如下generator.xml全部内容 <?xm ...
- XAMPP修改Apache默认网站目录htdocs的详解
XAMPP(Apache+MySQL+PHP+PERL)是一个功能强大的建 XAMPP 软件站集成环境包,大量站长在使用.正确安装好XAMPP后,默认是必须将php程序放到xampp\htdocs文件 ...
- 一文弄懂pytorch搭建网络流程+多分类评价指标
讲在前面,本来想通过一个简单的多层感知机实验一下不同的优化方法的,结果写着写着就先研究起评价指标来了,之前也写过一篇:https://www.cnblogs.com/xiximayou/p/13700 ...
- TypeScript 中限制对象键名的取值范围
当我们使用 TypeScript 时,我们想利用它提供的类型系统限制代码的方方面面,对象的键值,也不例外. 譬如我们有个对象存储每个年级的人名,类型大概长这样: type Students = Rec ...
- VS·卸载进程卡死"正在配置您的系统,这可能需要一些时间"
阅文时长 | 0.34分钟 字数统计 | 596.8字符 主要内容 | 1.引言&背景 2.声明与参考资料 『VS·卸载进程卡死"正在配置您的系统,这可能需要一些时间"』 ...
- [刷题] PTA 7-32 说反话-加强版
题目描述: 给定一个英语句子,各个单词之间用空格分隔.要求编写程序,将所有单词倒序输出 输入示例: Hello World Here I Come 输出示例: Come I Here World He ...
- 2019年又迎来Hi1620,鲲鹏920则是Hi1620系列的正式品牌和型号
据记者了解,2013年华为就发布了Hi1610,2014年的Hi1612是ARM64位CPU,2016年的Hi1616是首颗支持多路的ARM处理器,2019年又迎来Hi1620,鲲鹏920则是Hi16 ...
- CentOS7安装vncserver(启动失败及连接黑屏解决办法)
CentOS7安装vncserver(启动失败及连接黑屏解决办法) 转载weixin_34167043 最后发布于2017-11-09 15:11:00 阅读数 42 收藏 展开 AutoSAR入门 ...
- linux进阶之远程免密登录,动态添加磁盘及个别基础命令
一. 免密登录(远程连接ssh) ssh IP #连接登录到其它机 ssh 192.168.10.102 ssh IP "CMD" #在其它机器上执行命令 yum -y i ...
- 11.14 mii-tool:管理网络接口的状态
mii-tool命令用于查看.管理网络接口,默认情况下网卡的状态是自动协商的,但是有时也会出现不正常的情况,可以使用mii-tool进行调整. mii-tool [option] [interface ...