独占锁ReentrantLock
基本概念
-
ReentrantLock是一个可重入的互斥锁,所以称作 "独占锁"
- 可重入: ReentrantLock可以被单个线程多次获取
- 独占锁: ReentrantLock在同一个时间点只能被一个线程锁持有
-
锁是为了保护竞争资源,防止多个线程同时操作资源而出错
-
ReentrantLock在同一个时间点只能被一个线程获取,当某个线程获取到锁时,其余线程必须等待
-
ReentrantLock分为公平锁和非公平锁, 区别体现在获取锁的机制上是否公平 . ReentrantLock是通过一个FIFO等待队列来管理获取锁的所有线程的
- 在公平锁机制下: 线程必须依次排队获取锁
- 在非公平锁机制下: 在锁是可获取的状态时,无论线程是否在队列的开头都会获取锁
ReentrantLock函数列表
ReentrantLock
/**
* 创建一个ReentrantLock, 默认是非公平锁
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* 创建一个fair策略的ReentrantLock
* - fair为true表示公平锁
* - fair为false表示非公平锁
*
* @param fair 公平策略fair
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
getHoldCount
/**
* 获取当前线程持有锁的次数
*
* @return int 当前线程持有锁的次数
*/
public int getHoldCount() {
return sync.getHoldCount();
}
getOwner
/**
* 获取当前持有该锁的线程,如果该锁未被任何线程持有则返回null
*
* @return Thread 当前持有该锁的线程
*/
protected Thread getOwner() {
return sync.getOwner();
}
getQueuedThreads
/**
* 获取一个包含可能正在等待该锁的线程的集合Collection
*
* @return Collection<Thread> 一个包含可能正在等待该锁的线程的集合
*/
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
getQueueLength
/**
* 获取等待该锁的线程的可能的个数
*
* @return int 等待该锁的线程的可能个数
*/
public final int getQueueLength() {
return sync.getQueueLength();
}
getWaitingThreads
/**
* 获取一个可能正在等待该锁相关联的条件的线程的集合Collection
*
* @param condition 该锁相关联的条件
* @return Collection<Thread> 可能正在等待该锁相关联的条件的线程的集合Collection
*/
protected Collection<Thread> getWaitingThreads(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
}
getWaitQueueLength
/**
* 获取一个可能正在等待该锁相关联的条件的线程的个数
*
* @param condition 该锁相关联的条件
* @return Collection<Thread> 可能正在等待该锁相关联的条件的线程的个数
*/
public int getWaitQueueLength(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
}
hasQueuedThread
/**
* 判断给定的线程是否正在等待获取该锁
*
* @param thread 给定的线程
* @return boolean 给定的线程是否正在等待获取该锁
*/
public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
}
hasQueuedThreads
/**
* 判断是否存在正在等待获取该锁的线程
*
* @return boolean 是否存在正在等待获取该锁的线程
*/
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
hasWaiters
/**
* 判断是否存在正在等待该锁相关联的条件的线程
*
* @param condition 该锁相关联的条件
* @return boolean 是否存在正在等待该锁相关联条件的线程
*/
public boolean hasWaiters(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
}
isFair
/**
* 判断该锁是否是公平锁
* - 如果是公平锁则返回true
* - 如果是非公平锁则返回false
*
* @return boolean 该锁是否是公平锁
*/
public final boolean isFair() {
return sync instanceof FairSync;
}
isHeldByCurrentThread
/**
* 判断该锁是否被当前线程所持有
*
* @return boolean 该锁是否被当前线程所持有
*/
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
isLocked
/**
* 判断该锁是否被线程所持有
*
* @return boolean 该锁是否被线程所持有
*/
public boolean isLocked() {
return sync.isLocked();
}
lock
/**
* 获取一个锁
*/
public void lock() {
sync.lock();
}
lockInterruptibly
/**
* 获取一个锁,除非该当前线程被中断
* 即在当前线程未被中断的情况下获取一个锁
*/
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
newCondition
/**
* 创建一个Lock锁实例的条件Condition实例
*
* @return Condition 与Lock实例相关的Condition实例
*/
public Condition newCondition() {
return sync.newCondition();
}
tryLock
/**
* 在调用时,如果该锁未被其余线程持有的时候,获取该锁
*
* @return boolean 是否成功获取到该锁
*/
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
/**
* 如果当前线程在给定的时间内未被其余线程持有,并且当前线程未被中断,获取该锁
*
* @param timeout 给定的过期时间
* @param unit 给定的时间单位
* @return boolean 是否成功获取到该锁
*/
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
unlock
/**
* 试图释放该锁
* - 如果当前的线程时该锁的持有者,那么持有的次数将会减少
* - 如果持有的锁次数为0,那么就会释放该锁
*
*/
public void unlock() {
sync.release(1);
}
ReentrantLock示例
lock
- Depot:
class Depot {
/**
* 仓库的实际数量
*/
private int size;
/**
* 独占锁
*/
private Lock lock;
public Depot() {
this.size = 0;
// 通过ReentrantLock实现独占锁
this.lock = new ReentrantLock();
}
/**
* 生产
*
* @param val 生产的数量
*/
public void produce(int val) {
lock.lock();
try {
size += val;
System.out.printf("线程-%s生产%d个产品,仓库的实际数量为%d\n", Thread.currentThread().getName(), val, size);
} finally {
lock.unlock();
}
}
/**
* 消费
*
* @param val 消费的数量
*/
public void consume(int val) {
lock.lock();
try {
size -= val;
System.out.printf("线程-%s消费%d个产品,仓库的实际数量为%d\n", Thread.currentThread().getName(), val, size);
} finally {
lock.unlock();
}
}
}
- Producer:
class Producer {
private Depot depot;
public Producer() {
this.depot = depot;
}
public void produce(final int val) {
new Thread() {
public void run() {
depot.produce(val);
}
}.start();
}
}
- Consumer:
class Consumer {
private Depot depot;
public Consumer() {
this.depot = depot;
}
public void consume() {
new Thread() {
public void run(final int val) {
depot.consume(val);
}
}.start();
}
}
- LockTest:
public Class LockTest {
public void main(String[] args) {
Depot depot = new Depot();
Producer producer = new Producer(depot);
Consumer consumer = new Consumer(depot);
producer.produce(60);
producer.produce(120);
consumer.consume(90);
consumer.consume(150);
producer.produce(110);
}
}
线程-Thread-0生产60个产品,仓库的实际数量为60
线程-Thread-1生产120个产品,仓库的实际数量为180
线程-Thread-2消费90个产品,仓库的实际数量为90
线程-Thread-3消费150个产品,仓库的实际数量为-60
线程-Thread-4生产110个产品,仓库的实际数量为50
-
仓库Depot:
-
通过produce() 生产仓库中的货物
-
通过consume() 消费仓库中的货物
-
通过独占锁lock实现对仓库的互斥访问:
- 在进行生产或者消费操作仓库中的货品前,首先通过lock锁住仓库
- 在进行生产或者消费操作仓库中的货品后,再通过unlock解锁
-
-
生产者Producer:
- 通过调用Producer中的produce() 方法可以新建一个线程生产仓库中的货物
-
消费者Consumer:
- 通过调用Consumer中的consume() 方法可以新建一个线程消费仓库中的货物
-
客户端LockTest:
- 在客户端主线程main中,新建一个生产者producer和一个消费者consumer, 用于生产和消费仓库中的货物
-
该示例存在的问题:
- 现实中,仓库的货物数量不可能为负数,但是该示例模型中的仓库数量可以为负数,这与现实中的实际情况相矛盾
- 现实中,仓库的容量是有限制的,但是该示例模型中的仓库数量是没有限制的
unlock
- Depot: 去掉对仓库的生产和消费方法的锁
class Depot {
/**
* 仓库的实际数量
*/
private int size;
/**
* 独占锁
*/
private Lock lock;
public Depot() {
this.size = 0;
// 通过ReentrantLock实现独占锁
this.lock = new ReentrantLock();
}
/**
* 生产
*
* @param val 生产的数量
*/
public void produce(int val) {
size += val;
System.out.printf("线程-%s生产%d个产品,仓库的实际数量为%d\n", Thread.currentThread().getName(), val, size);
}
/**
* 消费
*
* @param val 消费的数量
*/
public void consume(int val) {
size -= val;
System.out.printf("线程-%s消费%d个产品,仓库的实际数量为%d\n", Thread.currentThread().getName(), val, size);
}
}
- Producer:
class Producer {
private Depot depot;
public Producer() {
this.depot = depot;
}
public void produce(final int val) {
new Thread() {
public void run() {
depot.produce(val);
}
}.start();
}
}
- Consumer:
class Consumer {
private Depot depot;
public Consumer() {
this.depot = depot;
}
public void consume() {
new Thread() {
public void run(final int val) {
depot.consume(val);
}
}.start();
}
}
- LockTest:
public Class LockTest {
public void main(String[] args) {
Depot depot = new Depot();
Producer producer = new Producer(depot);
Consumer consumer = new Consumer(depot);
producer.produce(60);
producer.produce(120);
consumer.consume(90);
consumer.consume(150);
producer.produce(110);
}
}
线程-Thread-0生产60个产品,仓库的实际数量为-60
线程-Thread-1生产120个产品,仓库的实际数量为-60
线程-Thread-2消费90个产品,仓库的实际数量为-60
线程-Thread-3消费150个产品,仓库的实际数量为-60
线程-Thread-4生产110个产品,仓库的实际数量为50
- 该示例去掉了仓库的生产和消费方法的锁,没有实现对仓库的互斥访问.导致出现意想不到的数据
Condition
-
Condition: 通过Condition增加锁的条件,解决以下两个问题
- 仓库的容量为负数
- 仓库的容量没有限制
-
Condition需要和Lock联合使用:
- 通过Condition的await() 方法,使得线程阻塞,类似于线程的wait()方法
- 通过Condition的signal() 方法,使得线程唤醒,类是于线程的notify()方法
-
Depot:
class Depot {
/**
* 仓库的容量
*/
private int capacity;
/**
* 仓库的数量
*/
private int size;
/**
* 独占锁
*/
private Lock lock;
/**
* 生产条件
*/
private Condition fullCondition;
/**
* 消费条件
*/
private Condition emptyCondition;
public Depot(int capacity) {
this.capacity = capacity;
this.size = 0;
this.lock = new ReetrantLock();
this.fullCondition = lock.newCondition();
this.emptyCondition = lock.newCondition();
}
/**
* 生产
*
* @param val 生产的数量
*/
public void produce(int val) {
lock.lock();
try {
// 想要生产的数量. 有可能想要生产的数量太多,需要多次生产
int want = val;
while (want > 0) {
// 当库存满时,等待消费者消费产品
while (size >= capacity) {
fullCondition.await();
/*
* 获取实际生产的数量,即库存中新增的数量:
* - 如果 库存 + 想要生产的数量 > 总的容量, 则 实际生产数量 = 总的容量 - 库存
* - 如果 库存 + 想要生产的数量 <= 总的容量, 则 实际生产的数量 = 想要生产的数量
*/
int increment = (size + want) > capacity ? (capacity - size) : want;
size += increment;
want -= increment;
System.out.printf("线程-%s生产%d个产品,剩余想要生产数量为%d,实际生产数量%d,仓库的实际数量为%d\n", Thread.currentThread().getName(), want, increment, val, size);
// 通知消费者进行消费
emptyCondition.signal();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* 消费
*
* @param val 消费的数量
*/
public void consume(int val) {
lock.lock();
try {
// want表示客户端想要消费的产品
int want = val;
while (want > 0) {
// 当仓库中的库存为0时,等待消费者生产产品
while (size <= 0) {
emptyCondition.await();
/*
* 获取实际消费的数量,即库存中减少的数量:
* - 如果 库存 < 想要消费的数量, 则 实际消费数量 = 库存
* - 如果 库存 >= 想要生产的数量, 则 实际消费的数量 = 想要消费的数量
*/
int decrement = size < want ? size : want;
size -= decrement;
want -= decrement
System.out.printf("线程-%s消费%d个产品,剩余想要消费数量为%d,实际消费数量为%d,仓库的实际数量为%d\n", Thread.currentThread().getName(), val, size);
// 通知生产者进行生产
fullCondition.signal();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
- Producer:
class Producer {
private Depot depot;
public producer(Depot depot) {
this.depot = depot;
}
/**
* 生产产品
*
* @param val 生产的产品数量
*/
public void produce(final int val) {
new Thread() {
public void run() {
depot.produce(val);
}
}.start();
}
}
- Customer:
class Customer {
private Depot depot;
public Customer(Depot depot) {
this.depot = depot;
}
/**
* 消费产品
*
* @param val 消费产品的数量
*/
public void consume(final int val) {
new Thread() {
public void run() {
depot.consume(val);
}
}.start();
}
}
- ConditionTest:
public class ConditionTest {
public static void main(String[] args) {
Depot depot = new Depot(100);
Producer producer = new Producer(depot);
Consumer consumer = new Consumer(depot);
producer.produce(60);
producer.produce(120);
consumer.consume(90);
consumer.consume(150);
producer.produce(110);
}
}
线程-Thread-0生产60个产品,剩余想要生产数量为0,实际生产的数量为60,仓库的实际数量为60
线程-Thread-1生产120个产品,剩余想要生产数量为80,实际生产的数量为40,仓库的实际数量为100
线程-Thread-2消费90个产品,剩余想要消费数量为0,实际消费的数量为90,仓库的实际数量为10
线程-Thread-3消费150个产品,剩余想要消费数量为140,实际消费的数量为10,仓库的实际数量为0
线程-Thread-4生产110个产品,剩余想要生产数量为10,实际生产的数量为100,仓库的实际数量为100
线程-Thread-3消费150个产品,剩余想要消费数量为40,实际消费的数量为100,仓库的实际数量为0
线程-Thread-4生产110个产品,剩余想要生产数量为0,实际生产的数量为10,仓库的实际数量为10
线程-Thread-3消费150个产品,剩余想要消费数量为30,实际生产的数量为10,仓库的实际数量为0
线程-Thread-1生产120个产品,剩余想要生产数量为0,实际生产的数量为80,仓库的实际数量为80
线程-Thread-3消费150个产品,剩余想要消费数量为0,实际消费的数量为30,仓库的实际数量为50