文章目录
ReentrantLock 介绍
- ReentrantLock是一个可重入的互斥锁,又被称为“独占锁”。
- ReentrantLock锁在同一个时间点只能被一个线程锁持有;可重入表示,ReentrantLock锁可以被同一个线程多次获取。
- ReentraantLock是通过一个FIFO的等待队列来管理获取该锁所有线程的。在“公平锁”的机制下,线程依次排队获取锁;而“非公平锁”在锁是可获取状态时,不管自己是不是在队列的开头都会获取锁。
ReentrantLock构造方法
/**
* 默认创建非公平锁
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* fair为true表示是公平锁,fair为false表示是非公平锁
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
ReentrantLock核心方法
/**
* 阻塞等待获取锁;不允许Thread.interrupt中断,即使检测到Thread.isInterrupted,一样会继续尝试 * 获取锁,失败则继续休眠。只是在最后获取锁成功后再把当前线程置为interrupted状态,然后再中断线程。
*/
public void lock() {
sync.lock();
}
/**
* 当前线程未被中断,则获取锁
* 允许在等待时由其它线程调用等待线程的Thread.interrupt方法来中断等待线程的等待而直接返回,这时 * 不用获取锁,而会抛出一个InterruptedException
*/
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
/**
*尝试申请一个锁,在成功获得锁后返回true,否则,立即返回false
*/
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
/**
* 在一段时间内尝试申请一个锁,在成功获得锁后返回true,否则,立即返回false
*/
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
/**
* 释放锁
*/
public void unlock() {
sync.release(1);
}
/**
* 条件实例
*/
public Condition newCondition() {
return sync.newCondition();
}
/**
* 获取当前线程持有此锁的次数
*/
public int getHoldCount() {
return sync.getHoldCount();
}
/**
* 是否被当前线程持有
*/
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
/**
* 查询此锁是否由任意线程持有
*/
public boolean isLocked() {
return sync.isLocked();
}
/**
*如果是“公平锁”返回true,否则返回false
*/
public final boolean isFair() {
return sync instanceof FairSync;
}
/**
* 获取目前拥有此锁的线程,如果此锁不被任何线程拥有,则返回 null
*/
protected Thread getOwner() {
return sync.getOwner();
}
/**
* 查询是否有线程正在等待
*/
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
/**
*查询给定线程是否正在等待获取此锁。
*/
public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
}
/**
* 获取正等待获取此锁的线程数
*/
public final int getQueueLength() {
return sync.getQueueLength();
}
/**
* 正等待获取此锁的线程集合
*/
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
/**
*是否存在正在等待并符合相关给定条件的线程
*/
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);
}
/**
* 正在等待并符合相关给定条件的线程数量
*/
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);
}
/**
* 正在等待并符合相关给定条件的线程集合
*/
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);
}
ReentrantLock和Synchronized比较
- synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。
- synchronized可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。
- synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以相应中断。
condition
- 用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition的await()、signal()这种方式实现线程间协作更加安全和高效。
应用场景
生产者和消费者
public class Consumer implements Runnable {
private List<Integer> container;
private ReentrantLock lock;
private Condition producerCondition;
private Condition consumerCondition;
public Consumer(List<Integer> container, ReentrantLock lock, Condition producerCondition, Condition consumerCondition) {
this.container = container;
this.lock = lock;
this.producerCondition = producerCondition;
this.consumerCondition = consumerCondition;
}
public void consumer() {
try {
//获得锁
lock.lock();
//容器空了,需要生产
if (container.size() == 0) {
System.out.println("消费完了,。。。。");
consumerCondition.await();
}
Integer p = container.remove(0);
//模拟1秒消费一个产品
TimeUnit.MILLISECONDS.sleep(1000);
System.out.println("消费产品:" + p);
//消费一个产品,通知生产者
producerCondition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放锁
lock.unlock();
}
}
@Override
public void run() {
while (true) {
consumer();
}
}
public static void main(String[] args) {
List<Integer> container = new ArrayList<>(5);
ReentrantLock lock = new ReentrantLock();
Condition producerCondition = lock.newCondition();
Condition consumerCondition = lock.newCondition();
Thread threadproduce = new Thread(new Producer(container, lock, producerCondition, consumerCondition));
Thread threadconsumer = new Thread(new Consumer(container, lock, producerCondition, consumerCondition));
threadproduce.start();
threadconsumer.start();
}
}
public Producer(List<Integer> container, ReentrantLock lock, Condition producerCondition, Condition consumerCondition) {
this.container = container;
this.lock = lock;
this.producerCondition = producerCondition;
this.consumerCondition = consumerCondition;
}
public void produce() {
//产品容器容量大小
int capacity = 5;
try {
//获得锁
lock.lock();
//容器满了,不在生产
if (container.size() == capacity) {
System.out.println("生产满了。。。。");
producerCondition.await();
}
Random random = new Random();
int p = random.nextInt(50);
//模拟1秒生产一个产品
TimeUnit.MILLISECONDS.sleep(1000);
System.out.println("生产产品:" + p);
container.add(p);
//生产一个产品,通知消费者
consumerCondition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放锁
lock.unlock();
}
}
@Override
public void run() {
while (true){
produce();
}
}
顺序打印ABC十遍
public class PrintABC {
private static ReentrantLock lock = new ReentrantLock();
private static Condition conditionA = lock.newCondition();
private static Condition conditionB = lock.newCondition();
private static Condition conditionC = lock.newCondition();
private static volatile int state = 1;
public static void main(String[] args) throws InterruptedException {
ExecutorService poolService = Executors.newFixedThreadPool(3);
Integer count = 10;
poolService.execute(new Worker("A", count, 1, lock, conditionA, conditionB));
poolService.execute(new Worker("B", count, 2, lock, conditionB, conditionC));
poolService.execute(new Worker("C", count, 3, lock, conditionC, conditionA));
Thread.sleep(5000);
poolService.shutdownNow();
}
public static class Worker implements Runnable {
private String key;
private Integer count;
private Lock lock;
private Condition current;
private Condition next;
private int targetState;
public Worker(String key, Integer count, int targetState, Lock lock, Condition cur, Condition next) {
this.key = key;
this.count = count;
this.lock = lock;
this.current = cur;
this.next = next;
this.targetState = targetState;
}
@Override
public void run() {
this.lock.lock();
try {
for (int i = 0; i < count; i++) {
while (state != targetState) {
current.await();
System.out.println(Thread.currentThread().getName()+"释放对象锁");
}
System.out.println(i + "," + key);
state++;
if (state > 3) {
state = 1;
}
next.signal();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
this.lock.unlock();
}
}
}
}
lock方法分析
ReenTrantLock的锁有两种实现方式:公平锁和非公平锁。两者都继承了Sync,Sync又继承了AbstractQueuedSynchronizer(AQS)。下面我们对lock方法做一个解析
//非公平锁
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
//利用CAS进行比较和交换,在AbstractQueuedSynchronizer中通过state值判断锁是否持有, //如果是1则锁持有,0表示锁释放。如果state>1表示一个锁被同一个线程持有多次,这里就是可重 //入的实现。
if (compareAndSetState(0, 1))
//设置状态值成功后则将当前线程设置拥有独占锁
setExclusiveOwnerThread(Thread.currentThread());
else
//如果当前锁已被其他线程持有,则调用acuire方法
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
public final void acquire(int arg) {
//再次尝试获取锁,如果还是没有获取到则加入到双向链表的尾部,并通过自旋等待获取锁,在一定条件下会停止自旋并中断当前线程
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//设置当前线程中断
selfInterrupt();
}
//公平锁,唯一的区别是这里不直接先通过CAS去获取锁
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
补充:
- AQS:基于CLH队列,用volatile修饰共享变量state,线程通过CAS去改变状态符,成功则获取锁成功,失败则进入等待队列,等待被唤醒。CLH队列是FIFO的双端双向队列。