• java
  • go
  • 数据库
  • linux
  • 中间件
  • 书
  • 源码
  • 夕拾

  • java
  • go
  • 数据库
  • linux
  • 中间件
  • 书
  • 源码
  • 夕拾

【第四部分】高级主题

目录

  • 目录
  • 显式锁
    • lock与reentranlock
      • 轮询锁与定时锁
      • 可中断的锁获取操作
      • 非块结构加锁
    • 性能
    • 公平性
    • synchronized与reeentranlock的选择
    • 读写锁
  • 构建自定义的同步结构
    • 状态依赖性的管理
      • 条件队列
    • 使用条件队列
      • 条件谓词
      • 过早唤醒
      • 丢失信号-活跃性故障
      • 通知
    • 显示的condition对象
    • synchronizer剖析
    • aqs

显式锁

lock与reentranlock

ReentranLock实现了Lock接口,提供了与synchronized相同的互斥性与内存可见性

ReentranLock提供了可重入的枷锁语句。

内置锁(synchronized)的灵活度较差,如:

  1. 无法中断一个正在获取锁的线程
  2. 无法再请求获取一个锁时,无限期等待下去.
  3. 内置锁必须再获取该锁的代码块中.虽然简化了编码,并且与异常处理操作实现了很好得交互,但是无法实现非阻塞结构得加锁规则.

轮询锁与定时锁

可定时与可轮询得锁获取模式是通过tryLock()方法实现的.它有更完善得错误恢复机制.

防止死锁的唯一方法就是在构造程序时避免出现不一致的锁顺序,可定时与可轮询的锁提供了另一种选择:避免死锁的发生.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
while(true){
try {
if(lock1.tryLock){
try{

if(lock2.tryLock){
dosth...
}
}finally{
lock2.unlock();
}
}
}finally{
lock1.unlock();
}
}

定时锁

1
2
3
4
5
6
7
8
9
10
11


if(!lock.tryLock(nanosToLock,NANOSECONDS)){
return false;
}
try{
doSth();
return true;
}finally{
lock.unlock();
}

可中断的锁获取操作

1
2
3
4
5
6
lock.lockInterruptibly();
try{

}finally {
lock.unlock();
}

非块结构加锁

[CPJ 2.5.1.4],连锁式加锁,锁耦合

性能

jdk6 reentreanLock与内置锁性能可伸缩性相当

公平性

reentranlock默认为非公平锁
公平锁,线程按照它们发出请求的顺序来获得锁
非公平锁性能高于公平锁的原因,在恢复一个被挂起的线程与该线程真正开始运行之间存在严重的延迟.公平锁会将性能降低约2个数量级

当持有锁的时间较长,或者请求锁的平均时间间隔较长时可以用公平锁

synchronized与reeentranlock的选择

在一些内置锁无法满足的情况下,reentranLock可以作为一种高级工具.
需要高级功能时才需要使用ReentranLock
功能包括:可定时,可轮询,可中断的锁操作,公平队列,非块结构加锁,否则还是优先内置锁

读写锁

  • 释放优先
  • 读线程插队
  • 重入性
  • 降级
  • 升级

当访问以读取作为主的数据结构时,读写锁可增加程序伸缩性.

构建自定义的同步结构

使用java的类库提供的底层机制来构造自己的同步机制,包括内置的条件队列,显式的Condition对象以及AbstractQueuedSynchronizer框架.

状态依赖性的管理

依赖状态的操作可以一直阻塞,直到可以继续执行.
内置的条件队列可以使线程一直阻塞,直到对象进入某个线程可以继续执行的状态,当被阻塞的线程可以执行时,再唤醒它们.

条件队列

所谓条件队列:使得一组线程,能够通过某种方式来等待特定的条件变成真.
传统队列中元素是一个个数据,而条件队列中是一个个等待相关条件的线程.

使用 notify,wait,notifyAll

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

public synchronized void put(V v){
while(isFull()){
wait();
}
doPut(v);
notifyAll();
}

public synchronized V take(){

while(isEmpty()){
wait();
}
V v = doTake();
notifyAll();
return v;
}

使用条件队列

条件谓词

条件谓词:使某个操作成为状态依赖操作的前提条件.
如:在有界队列中,只有当队列不为空,take方法才能执行.对take方法来说,条件谓词就是”队列不为空”

将与条件队列相关联的条件谓词以及在这些谓词上等待的操作都写入文档

过早唤醒

没当线程从wait中唤醒时,都必须再次测试条件谓词,如果谓词不为真,那么失败或者继续等待。
条件等待标准形式

1
2
3
4
5
6
7
void stateDependentMethod(){
synchronized(lock){
while(!conditionPredicate()){
lock.wait();
}
}
}

当使用条件等待(如Object.wait或者Conditon.await)

  • 通常都有条件谓词
  • 调用wait之前测试谓词,并且从wait中返回时再次测试
  • 在一个循环中用wait
  • 确保使用与条件队列相关的锁来保护构成谓词的各个状态变量
  • 调用wait,notify,notifyAll时一定要持有与队列相关的锁
  • 检查条件谓词之后以及开始执行相应的操作之前,不要释放锁

丢失信号-活跃性故障

丢失信号:线程必修等待一个已经成为真的条件,但在开始等待之前没有检查条件谓词.

通知

每当在等待一个条件时,一定要确保在条件谓词变为真时通过某种方式发出通知.

通知选择用notifyAll,仅在以下情况用notify

  • 所有等待的线程都相同.
  • 单进单出

显示的condition对象

内置条件队列存在一些缺陷:

  • 每个内置锁都只能有一个相关联的条件队列.

显示的Lock+condition更加灵活.

对于每个lock,可以有任意数量的condition对象.
condition对象继承了相关的lock对象的公平性,对于公平的锁,线程会依照FIFO顺序从condition.await中释放

condition与内置条件队列
condition.await == Object.wait
condition.singal == Object.notify

  • 类似 synchronzied与reentrantLock之间选择类似,如果需要高级功能,选择condition,如果使用了reentrantLock,那么就已经做出了选择.

synchronizer剖析

semaphore与reentrantLock都实现了基类AbstractQueuedSynchronizer(AQS).

AQS是一个用于构建锁和同步器的框架.

AQS不仅能极大减少实现工作,而且也不必处理在多个位置上发生的竞争问题.

  • 在基于AQS构建的同步器中,只可能在一个时刻发生阻塞,从而降低上下文切换的开销,提高吞吐量.

aqs

filebeat的配置
【第三部分】活跃性、性能与测试
  1. 1. 目录
  2. 2. 显式锁
    1. 2.1. lock与reentranlock
      1. 2.1.1. 轮询锁与定时锁
      2. 2.1.2. 可中断的锁获取操作
      3. 2.1.3. 非块结构加锁
    2. 2.2. 性能
    3. 2.3. 公平性
    4. 2.4. synchronized与reeentranlock的选择
    5. 2.5. 读写锁
  3. 3. 构建自定义的同步结构
    1. 3.1. 状态依赖性的管理
      1. 3.1.1. 条件队列
    2. 3.2. 使用条件队列
      1. 3.2.1. 条件谓词
      2. 3.2.2. 过早唤醒
      3. 3.2.3. 丢失信号-活跃性故障
      4. 3.2.4. 通知
    3. 3.3. 显示的condition对象
    4. 3.4. synchronizer剖析
    5. 3.5. aqs
© 2023 haoxp
Hexo theme