Java并发显式锁和显式条件队列
一 显式锁
在类中利用synchronized修饰的方法或者this代码块,均使用的是类的实例锁或者类的锁。这些锁都称为内置锁。
可以利用显式锁进行协调对象的访问。即ReentrantLock。这是一种可以提供无条件,可轮询,定时以及可中断的锁获取操作。对于锁的所有加锁和解锁都是显式的。常规的内置锁是无法中断一个正在等待获取锁的线程,以及无法在请求获取一个锁的无限等待下去。
ReentrantLock标准使用方式
1
2
3
4
5
6
7
|
Lock new ReentrantLock(); lock.lock(); try { //具体任务 } finally { lock.unlock(); } |
由于内置锁中防止死锁的发生就是避免出现锁的顺序不当导致的。利用这显式锁可以避免死锁的发生。当不能要获取所有的锁的时候,那么就可以利用显式的方法来释放已经有的锁。然后再轮询的重新获取锁的控制权。
几个关键的方法说明:
lock():阻塞的方法
如果该锁没有被任何线程所拥有,则获取锁,立即返回,锁计数器加1.
如果该锁已被当前线程所持有,则立即返回,锁计数器加1.
如果该锁已被其他线程所拥有,则禁用当前线程,使得其休眠阻塞。
tryLock(time):可阻塞可中断的方法
如果该锁在给定时间内没有被任何线程所持有,且当前线程没有被中断,则获取锁,立即返回true,锁计数器加1.
如果该锁已被当前线程所持有,则立即返回true,锁计数器加1.
如果该锁已被其他线程所拥有,则禁用当前线程,使其休眠阻塞,直到发生以下情况:
当前线程获取锁,当前线程被中断,时间到了
返回状态:成功获取锁或者已经持有,则返回true。时间到了还没有获取锁,则返回false
lockInterruptibly():可阻塞可中断的方法
这种锁的获取必须在当前线程没有被中断的时候才能尝试获取锁
如果该锁没有被任何线程所拥有,则获取锁,立即返回,锁计数器加1.
如果该锁已被当前线程所持有,则立即返回,锁计数器加1.
如果该锁已被其他线程所拥有,则禁用当前线程,使得其休眠阻塞,直到发生以下情况:
当前线程获取锁,当前线程被中断
tryLock():非阻塞的方法
如果该锁没有被任何线程保持,则获取锁,立即返回true,锁计数器加1.
如果该锁已被当前线程所持有,则立即返回true,锁计数器加1.
如果该锁已被其他线程所拥有,则立即返回false。
unlock():释放锁
如果当前线程是此锁所有者,则将保持计数减 1。如果保持计数现在为 0,则释放该锁
在利用ReentrantLock构造函数的时候,可以传递一个boolean,当不传入模式是false,即创建一个非公平锁,当传入true的时候,创建一个公平的锁。
所谓的公平的锁,线程按照它们发出请求的顺序来获得锁。但在非公平的锁上,允许刚请求的锁直接马上获取锁。一般非公平锁的性能要高于公平锁的性能,这是因为恢复一个被挂起的线程以及真正开始运行需要一点时间,如果在此刻能将这段时间让立刻要获取锁的线程,则就会提高吞吐量。
synchronized与ReentrantLock的选择
一般直接使用synchronized,只有需要使用到可定时的,可轮询的,可中断的锁获取操作,公平队列的时候才可以考虑使用ReentrantLock。
在对于读写操作中,也可以利用显式锁中的读-写锁进行,即ReentrantReadWriteLock.
二 显式条件队列
可以利用wait,notify,notifyAll实现自定义同步的过程(详见我的系列的多线程设计模式)。一般利用这种方式将内置锁与内置条件队列统一在一个类中,即一个类即是内置锁,又充当了条件队列了。凡是对于该类的wait都处于这个类的专有wait
set中,这里将wait set称为条件队列。
条件队列指的就是它使得一组线程集合能够通过某种方式来等待特定的条件成真。这里的条件队列里存放的是一个个正在等待条件成真的线程。
所有等待的线程都必须要等待某种条件成真才进而执行。对于这种条件称作为条件谓词(或警戒条件)。条件谓词是依赖于许多个状态变量的,对于这些状态变量必须要由一个锁来保护,在每次测试条件谓词的时候必须先获取该锁,则就要求了锁对象和条件队列对象必须是同一个对象。
状态依赖的标准形式:这里和多线程设计模式中的Guarded Suspension Pattern一样的形式。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<pre class = "brush:java;toolbar:false;" > public synchronized void concreteWork() { while (!condition) { try { wait(); //此时当前线程就进入了该this对象的条件队列中等候 } catch (InterruptedException { } //具体的工作,当条件谓词成功后执行的 } //必须要保证在调用wait之前和从wait唤醒后都要对于条件谓词测试 |
状态通知的形式:
每当在等待一个条件的时候,一定要保证在条件谓词变成真的时候通过某种方式发出通知。一般就是利用Object的notifyAll和notify来实现。一般采用notifyAll来实现,虽然可能会导致上下文切换等性能。但是这种能保证所有的线程都能进行公平竞争,防止信号丢失。
一般多个线程可能会在同一个对象的同一个条件队列上等待多个不同的添加谓词。使用notify可能会唤醒本不该唤醒的线程。譬如,当线程A在条件队列中等待条件谓词PA,线程B在条件队列中等待条件谓词PB,若线程C执行了操作,使得PB变成真,此时利用notify,可能会唤醒线程A,A被唤醒发现PA还是不为真,则继续等待,此时线程B则以为PB没有真,就会彻底的丢失了信号,从而再次等待下去
使用notify的情况仅仅同时满足“所有等待线程都是等待相同的条件谓词”和“每次最多只能唤醒一个线程来执行”。
ReentrantLock是Lock的子类,它是一种广义的内置锁,Condition则是一种广义的内置条件队列。由于多个线程可能会在同一个条件队列中等待不同的条件谓词,有时候为了方便的进行管理,可以针对不同的条件谓词设置不同的条件队列来等待。
一般一个Condition和一个Lock关联在一起,就是如同一个内置锁和内置条件队列关联在同一个对象中。一般创建Condition,就是在需要关联的Lock上调用Lock.newCondition方法。一个Lock则可以对应多个Condition对象。在Condition中,与Object中wait,notify,notifyAll所对应的方法分别为await,signal,signalAll。
显式的Condition和内置条件队列的选择:
一般还是选择内置条件队列,当需要使用公平队列或在每个锁上对应多个等待线程集的时候,就可以运用Condition了
代码实例:一个利用Condition和Lock来实现的有界缓存,里面有两种条件队列
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
package whut.usersynchrnoized; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; //利用Condition和Lock来实现有界缓存 public class ConditionBoundedBuffer<T> private final Lock new ReentrantLock(); private final Condition private final Condition private final T[] private int tail, public ConditionBoundedBuffer( int size) items new Object[size]; } // public void put(T throws InterruptedException lock.lock(); try { while (count full.await(); // items[tail] if (++tail tail 0 ; ++count; empty.signal(); // } finally { lock.unlock(); } } // public T throws InterruptedException lock.lock(); try { while (count 0 ) empty.await(); // T items[tail] null ; if (++head head 0 ; --count; full.signal(); // return x; } finally { lock.unlock(); } } } |
本文出自 “在云端的追梦” 博客,请务必保留此出处http://computerdragon.blog.51cto.com/6235984/1216434
Java并发显式锁和显式条件队列的更多相关文章
- Java并发之显式锁和隐式锁的区别
Java并发之显式锁和隐式锁的区别 在面试的过程中有可能会问到:在Java并发编程中,锁有两种实现:使用隐式锁和使用显示锁分别是什么?两者的区别是什么?所谓的显式锁和隐式锁的区别也就是说说Synchr ...
- Java并发编程:锁的释放
Java并发编程:锁的释放 */--> code {color: #FF0000} pre.src {background-color: #002b36; color: #839496;} Ja ...
- java并发编程(6)显式锁
显式锁 一.Lock与ReentrantLock Lock提供了一种无条件的.可轮询的.定时的以及可中断的锁获取操作,所有的加锁和解锁方法都是显式的 ReentrantLock实现了Lock:并提供了 ...
- Java并发编程-各种锁
安全性和活跃度通常相互牵制.我们使用锁来保证线程安全,但是滥用锁可能引起锁顺序死锁.类似地,我们使用线程池和信号量来约束资源的使用, 但是缺不能知晓哪些管辖范围内的活动可能形成的资源死锁.Java应用 ...
- Java并发编程之锁机制
锁分类 悲观锁与乐观锁 悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改.因此对于同一个数据的并发操作,悲观锁采取加锁的形式.悲观的认为,不加锁的并发操作一定会出问题 ...
- Java并发:乐观锁
作者:汤圆 个人博客:javalover.cc 简介 悲观锁和乐观锁都属于比较抽象的概念: 我们可以用拟人的手法来想象一下: 悲观锁:像有些人,凡事都往坏的想,做最坏的打算:在java中就表现为,总是 ...
- JAVA 并发:CLH 锁 与 AbstractQueuedSynchronizer
首先向Doug Lea致敬. CLH 以下是CLH锁的一个简单实现: class SimpleCLHLock { /** * initialized with a dummy node */ priv ...
- java 并发——内置锁
坚持学习,总会有一些不一样的东西. 一.由单例模式引入 引用一下百度百科的定义-- 线程安全是多线程编程时的计算机程序代码中的一个概念.在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同 ...
- JAVA并发-内置锁和ThreadLocal
上一篇博客讲过,当多个线程访问共享的可变变量的时候,可以使用锁来进行线程同步.那么如果线程安全性存在的3个前提条件不同时存在的话,自然就不需要考虑线程安全性了.或者说如果我们能够将某个共享变量变为局部 ...
- java并发编程:锁的相关概念介绍
理解同步,最好先把java中锁相关的概念弄清楚,有助于我们更好的去理解.学习同步.java语言中与锁有关的几个概念主要是:可重入锁.读写锁.可中断锁.公平锁 一.可重入锁 synchronized和R ...
随机推荐
- c++学习笔记(一):内存分区模型
目录 内存分区模型 程序运行前 程序运行后 new操作符 内存分区模型 c++在执行时,将内存大方向划分为4个区域 代码区:存放函数体的二进制代码,由操作系统进行管理(编写的所有代码都会存放到该处) ...
- Json转实体类问题
背景:使用一个实体类,将json及xml转成对应的实体类 Transformers.fromJson 将json映射成对应的实体类, 原本已经测试,传xml是可以的,传的有字段及list<E&g ...
- 扩展KMP (ex_KMP)
一些约定: 字符串下标从1开始 s[1,i]表示S的第一个到第i个字符组成的字符串 解决的题型: 给你两个字符串A,B(A.size()=n,B.size()=m),求p数组 p[i]表示最大的len ...
- WebShell流量特征检测_冰蝎篇
80后用菜刀,90后用蚁剑,95后用冰蝎和哥斯拉,以phpshell连接为例,本文主要是对这四款经典的webshell管理工具进行流量分析和检测. 什么是一句话木马? 1.定义 顾名思义就是执行恶意指 ...
- chrome 被hao123 劫持处理
打开chrome,就进入baidu.com/xxx,烦人,浏览器被劫持了XXXX 查注册表hao123,删除找到的 进入chrome设置,修改主页新标签页 装杀毒软件,查杀病毒 修改chrome名 等 ...
- ASP.NET Core – Dependency Injection
前言 很久很久以前就写过了 Asp.net core 学习笔记 ( DI 依赖注入 ), 这篇只是整理一下而已. 参考 Using dependency injection in a .Net Cor ...
- Mininet安装记录
安装环境: Ubuntu虚拟机版本:14.04 Mininet版本:2.3.1b1 1.更改软件镜像源 在设置中进行如下操作: 选择国内的镜像站点,如阿里云. 点击关闭后,在弹出的窗口中点击重新载入, ...
- 【原创】解决NasCab掉进程,NasCab进程维护
最近对象吐槽家里服务器又连不上,看不了考研视频了. 我掏出手机一试,确实连不上.家里的服务器是Win11平台,用NasCab管理的视频文件,然后通过frpc做的内网穿透. 我们在外面的图书馆,连不上无 ...
- 【赵渝强老师】MongoDB的inMemory存储引擎
一.MongoDB的存储引擎概述 存储引擎(Storage Engine)是MongoDB的核心组件,负责管理数据如何存储在硬盘(Disk)和内存(Memory)上.从MongoDB 3.2 版本开始 ...
- aarch64 和 ARMV8 的区别
aarch64 和 ARMv8 是紧密相关但涵义不同的术语,在解释他们的区别之前,让我们先简单理解它们各自的含义: ARMv8: ARMv8 是指 ARM 架构的第八个版本,这是由 ARM Holdi ...