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关键字以及锁对象,配备了 ...
随机推荐
- 一文详解MySQL如何同时自增自减多个字段
本文将带大家聊一下如何同时自增自减多个字段 开始之前,先分享一套MySQL教程,小白入门或者学习巩固都可以看 MySQL基础入门-mysql教程-数据库实战(MySQL基础+MySQL高级+MySQL ...
- C++ primer plus读书笔记——第12章 类和动态内存分配
第12章 类和动态内存分配 1. 静态数据成员在类声明中声明,在包含类方法的文件中初始化.初始化时使用作用域运算符来指出静态成员所属的类.但如果静态成员是整形或枚举型const,则可以在类声明中初始化 ...
- linux环境下/etc/hosts文件详解
linux环境下/etc/hosts文件详解 就没一个昵称能用关注 0.0632017.09.12 17:04:28字数 623阅读 27,096 介绍 hosts文件是linux系统中负责ip地址与 ...
- MySQL给某个用户给某个库表设置权限
-- 用root(最高权限的用户)进行以下操作-- 创建数据库:emc_power CREATE DATABASE emc_power DEFAULT CHARACTER SET utf8 COLLA ...
- Linux硬件与服务
Linux硬件与服务 Linux Linux硬件与服务 1 Linux磁盘管理与磁盘结构 磁盘的组成结构 盘片的逻辑结构 分区格式化 实例说明: 2 磁盘管理之Block.iNode. super.s ...
- 2.9高级变量类型操作(列表 * 元组 * 字典 * 字符串)_内置函数_切片_运算符_for循环
高级变量类型 目标 列表 元组 字典 字符串 公共方法 变量高级 知识点回顾 Python 中数据类型可以分为 数字型 和 非数字型 数字型 整型 (int) 浮点型(float) 布尔型(bool) ...
- STM32的三种启动方式
- 手机POS机
资质查询 http://www.pbc.gov.cn/zhengwugongkai/127924/128041/2951606/1923625/1923629/d6d180ae/index4.html ...
- 1. 回顾Servlet
回顾Servlet 创建web工程 servlet-api:http://dwz.date/aTGa 编写Servlet import javax.servlet.ServletException; ...
- Guava Cache,Java本地内存缓存使用实践
Guava Cache,网上介绍很多,我就不赘述了. 分享一篇好的文章: Guava Cache内存缓存使用实践-定时异步刷新及简单抽象封装 Google Guava 3-缓存 在原作者基础上,我做了 ...