Java多线程实战(十二): 明白的锁机制详解
最编程
2024-07-28 19:41:51
...
显示锁:
Lock与ReentrantLock:
- Lock接口
void lock(); //获取锁
void lockInterruptibly() throws InterruptedException; //获取锁,且当前线程可被中断
boolean tryLock(); //尝试获取锁,true获取到锁, false未获取到锁
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock(); //释放锁
Condition newCondition(); //在当前锁上创建一个等待条件
- Lock的标准用法
Lock lock = new ReentrantLock();
lock.lock();
try{
// to do sth.
} finally{
lock.unlock(); //须要在finally中释放锁
}
轮询锁与定时锁:
- 轮询锁和定时锁可由tryLock来实现。
- 轮询锁,定时锁可以避免死锁的发生。
- 由tryLock实现轮询锁
public boolean transferMoney(Account fromAcct,
Account toAcct,
int amount,
long timeout,
TimeUnit unit){
long fixedDelay = getFixedDelayComponentNanos(timeout, unit);
long randMod = getRandomDelayModulusNanos(timeout, unit);
long stopTime = System.nanoTime() + unit.toNanos(timeout);
while (true){
if (fromAcct.lock.tryLock()){ //若获取到源账户锁
try{
if (toAcct.lock.tryLock()){ //若获取到目的账户锁
try{
if (fromAcct.getBalance() < amount){
throw new RuntimeException("money.not.enough");
} else{
fromAcct.debit(amount);
toAcct.credit(amount);
return true;
}
} finally{
toAcct.lock.unlock();
}
}
} finally{
fromAcct.lock.unlock();
}
}
if (System.nanoTime() < stopTime){
return false;
}
try {
Thread.sleep(fixedDelay + rand.nextLong()%randMod);
} catch (InterruptedException e) {
}
}
}
你也可以采用定时实现:
lock.tryLock(timeout, unit);
可中断的锁获取操作:
lock.lockInterruptibly();
try{
// maybe throws InterruptedException
doSomething();
} finally{
lock.unlock();
}
非块结构的加锁:
- 如ConcurrentHashMap中的分段锁实现。
性能考虑因素:
void lock(); //获取锁
void lockInterruptibly() throws InterruptedException; //获取锁,且当前线程可被中断
boolean tryLock(); //尝试获取锁,true获取到锁, false未获取到锁
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock(); //释放锁
Condition newCondition(); //在当前锁上创建一个等待条件
Lock lock = new ReentrantLock();
lock.lock();
try{
// to do sth.
} finally{
lock.unlock(); //须要在finally中释放锁
}
public boolean transferMoney(Account fromAcct,
Account toAcct,
int amount,
long timeout,
TimeUnit unit){
long fixedDelay = getFixedDelayComponentNanos(timeout, unit);
long randMod = getRandomDelayModulusNanos(timeout, unit);
long stopTime = System.nanoTime() + unit.toNanos(timeout);
while (true){
if (fromAcct.lock.tryLock()){ //若获取到源账户锁
try{
if (toAcct.lock.tryLock()){ //若获取到目的账户锁
try{
if (fromAcct.getBalance() < amount){
throw new RuntimeException("money.not.enough");
} else{
fromAcct.debit(amount);
toAcct.credit(amount);
return true;
}
} finally{
toAcct.lock.unlock();
}
}
} finally{
fromAcct.lock.unlock();
}
}
if (System.nanoTime() < stopTime){
return false;
}
try {
Thread.sleep(fixedDelay + rand.nextLong()%randMod);
} catch (InterruptedException e) {
}
}
}
lock.tryLock(timeout, unit);
lock.lockInterruptibly();
try{
// maybe throws InterruptedException
doSomething();
} finally{
lock.unlock();
}
- 上图是对HashMap的测试,可见在jdk1.5时ReentrantLock比内置锁吞吐量高,jdk1.6差异就很小了。
- 性能是一个不断变化的指标,如果在昨天的测试中发现X比Y更快,那么在今天就可能已经过时了。
公平性:
- ReentrantLock可初始化为公平或非公平的锁。
- 大多数情况下非公平锁的性能高于公平锁的性能。
- 基于公平锁,非公平锁及ConcurrentHashMap对HashMap进行吞吐量测试。
- 当持有锁的时间相对较长,或者请求锁的平均时间间隔较长,那么应该使用公平锁。
在Synchronized和ReentrantLock之间作出选择:
- 在一些内置锁无法满足需求的情况下,ReentrantLock可以作为一种高级工具。当需要一些高级功能时才应该使用ReentrantLock,这些功能包括:可定时,可轮询,可中断,公平队列,及非块结构的锁。否则还是应该优先使用synchronized.
读--写锁:
//读写锁允许同时多个线程读, 或最多一个线程写
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
- 读写锁的可选实现:
1. 释放优先。当写入锁释放后,应该优先选择读线程,写线程,还是最先发出请求的线程?
2. 读线程插队。锁由读线程持有,写线程再等待,再来一个读线程,是继续让读线程访问,还是让写线程访问.
3. 重入性。读取锁和写入锁是否可重入?
4. 降级。将写入锁降级为读取锁。
5. 升级。将读取锁升级为写入锁。
- 当锁的持有时间较长并且大部分操作都不会修改被守护的资源时,可用读写锁提高并发性。
/**
* 读写锁来包装Map
*/
public class ReadWriteMap<K, V> {
private final Map<K, V> map;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock r = lock.readLock();
private final Lock w = lock.writeLock();
public ReadWriteMap(Map<K, V> map){
this.map = map;
}
// 其他写操作...
public V put(K key, V value){
w.lock(); //请求写锁
try{
return map.put(key, value);
} finally{
w.unlock(); //勿忘
}
}
// 其他读操作...
public V get(K key){
r.lock(); //请求读锁
try{
return map.get(key);
} finally{
r.unlock(); //勿忘
}
}
}
- 对ArrayList使用ReentrantLock和ReadWriteLock测试吞吐量。
不吝指正。
推荐阅读
-
探索Java多线程中的Lock锁机制
-
Java多线程实战(十二): 明白的锁机制详解
-
Java多线程中的锁机制详解
-
.NET Core里的多线程实战(4): 了解和使用锁机制
-
深入理解Java锁接口(Lock)的工作机制与实战示例
-
详解Redis实战(十二):Redisson分布式锁中的守望者功能
-
理解Java文件锁FileLock在多线程环境下的工作机制详解
-
【2022新手指南】Java编程进阶之路 - 六、技术架构篇 ### MySQL索引底层解析与优化实战 - 你会讲解MySQL索引的数据结构吗?性能调优技巧知多少? - Redis深度揭秘:你知道多少?从基础到哨兵、主从复制全梳理 - Redis持久化及哨兵模式详解,还有集群搭建和Leader选举黑箱打开 - Zookeeper是个啥?特性和应用场景大公开 - ZooKeeper集群搭建攻略及 Leader选举、读写一致性、共享锁实现细节 - 探究ZooKeeper中的Leader选举机制及其在分布式环境中的作用 - Zab协议深入剖析:原理、功能与在Zookeeper中的核心地位 - RabbitMQ全方位解读:工作模式、消费限流、可靠投递与配置策略 - 设计者视角:RabbitMQ过期时间、死信队列与延时队列实践指南 - RocketMQ特性和应用场景揭示:理解其精髓与差异化优势 - Kafka详细介绍:特性及广泛应用于实时数据处理的场景解析 - ElasticSearch实力揭秘:特性概述与作为搜索引擎的广泛应用 - MongoDB认知升级:非关系型数据库的优势阐述,安装与使用实战教学 - BIO/NIO/AIO网络模型对比:掌握它们的区别与在网络编程中的实际应用 - Netty带你飞:理解其超快速度背后的秘密,包括线程模型分析 - 网络通信黑科技:Netty编解码原理与常用编解码器的应用,Protostuff实战演示 - 解密Netty粘包与拆包现象,怎样有效应对这一常见问题 - 自定义Netty心跳检测机制,轻松调整检测间隔时间的艺术 - Dubbo轻骑兵介绍:核心特性概览,服务降级实战与其实现益处 - Dubbo三大神器解读:本地存根与本地伪装的实战运用与优势呈现 ----------------------- 七、结语与回顾
-
深入理解Java中的锁机制:原理、优化策略、CAS与AQS详解