前言
内置锁虽然方便但限制很多:
- 一个线程因为等待内置锁而进入阻塞之后,就无法中断该线程了
- 尝试获取内置锁时,无法设置超时
- 获得内置锁,必须使用synchronized块
...
一、使用ReetrantLock
ReetrantLock 提供显式的 lock
和 unlock
方法
(1)可中断的锁
(2)超时
ReentrantLock 可以为获取锁的操作设置超时时间
lock.tryLock(1000, TimeUnit.MILLISECONDES);
(3)交替锁(head-over-hand locking)
在链表中插入一个节点。
1. 用锁保护整个链表(但链表加锁时其他使用者无法访问链表)
2. 交替锁可以只锁住链表的一部分(允许不涉及被锁部分的其他线程自由访问链表)
(4)条件变量
并发编程经常需要等待某个事件发生。
- 从队列删除元素前需要等待队列非空
- 向缓存添加数据前需要等待缓存有足够的空间
当另一个线程调用了signal() 或者 signalAlll(),意味着对应的条件可能变为真,await()将原子地回复运行并重新加锁。
二、原子变量
关于之前自增,java.util.concurrent.atomic包提供了更好的方案:
AtomicInteger 的 incrementAndGet()方法功能上等价于++count(AtomicInteger 也提供了getAndIncrement方法,等价于count++)。不过与++count 不同,incrementAndGet()方法是原子操作。
原子变量是无锁(lock-free)非阻塞(non-blocking)算法的基础,这种算法可以不用锁和阻塞来达到同步的目的。
三、再想想
- ReentrantLock创建时可以设置一个描述公平性的变量。什么是“公平”的锁?何时适合使用公平的锁?使用非公平的锁会怎样?
- 什么是ReentrantReadWriteLock?它与ReentrantLock有什么区别?使用与什么场景?
- 什么是“虚假唤醒”(spurious wakeup)什么时候发生虚假唤醒?为什么符合规范的代码不用担心虚假唤醒?
- 什么是AtomicIntegerFieldUpdater?它与AtomicInteger有什么区别?适合用于什么场景?
四、小结
ReentrantLock和java.util.concurrent.atomic可以做:
- 在线程获取锁时中断它
- 设置线程获取锁的超时时间
- 按任意顺序获取和释放锁
- 用条件变量等待某个条件变成真
- 使用原子变量避免锁的使用