1、轻量级锁
2、可重入(通过state计数)、可中断、可限时
3、支持公平锁和非公平锁
4、支持唤醒指定线程——condition
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
/** Synchronizer providing all implementation mechanics */
private final Sync sync;
public ReentrantLock() {
//默认非公平锁
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
}
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();
//非公平方式尝试获取锁
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//state为0时,直接cas,成功就设置独占
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
//state不为0,如果是当前线程持有,则state+1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//state不为0,且不是当前线程持有,返回false
return false;
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
//完全释放
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
}
只要记住,非公平就是先cas获取state,如果成功,相当于插队成功,也就是非公平;如果失败,老老实实排队
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//详见AQS那篇文章中的acquire方法,主要做了3件事:
//1、调用实现类的tryAcquire();2、addWaiter()入队;3、acquireQueued()公平获取锁
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
公平就是先看有没有人排队,没人排队就cas获取state,有人排队就跟在尾部排队
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
public void lock() {
sync.lock();
}
// 尝试获取锁,立即返回获取结果 轮询锁
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
//尝试获取锁,最多等待 timeout 时长 超时锁
public void unlock() {
sync.release(1);
}
//可中断锁,调用线程 interrupt 方法,则锁方法抛出 InterruptedException 中断锁
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
Condition与重入锁是通过lock.newCondition()方法产生一个与当前重入锁绑定的Condtion实例,我们通知该实例来控制线程的等待与通知。
阻塞队列JUC并发包之阻塞队列LinkedBlockingQueue、LinkedBlockingDeque和ArrayBlockingQueue,就是通过Condition来实现生产者消费者模式的。
public interface Condition {
//使当前线程加入 await() 等待队列中,并释放当锁,当其他线程调用signal()会重新请求锁。与Object.wait()类似。
void await() throws InterruptedException;
//调用该方法的前提是,当前线程已经成功获得与该条件对象绑定的重入锁,否则调用该方法时会抛出IllegalMonitorStateException。
//调用该方法后,结束等待的唯一方法是其它线程调用该条件对象的signal()或signalALL()方法。等待过程中如果当前线程被中断,该方法仍然会继续等待,同时保留该线程的中断状态。
void awaitUninterruptibly();
// 调用该方法的前提是,当前线程已经成功获得与该条件对象绑定的重入锁,否则调用该方法时会抛出IllegalMonitorStateException。
//nanosTimeout指定该方法等待信号的的最大时间(单位为纳秒)。若指定时间内收到signal()或signalALL()则返回nanosTimeout减去已经等待的时间;
//若指定时间内有其它线程中断该线程,则抛出InterruptedException并清除当前线程的打断状态;若指定时间内未收到通知,则返回0或负数。
long awaitNanos(long nanosTimeout) throws InterruptedException;
//与await()基本一致,唯一不同点在于,指定时间之内没有收到signal()或signalALL()信号或者线程中断时该方法会返回false;其它情况返回true。
boolean await(long time, TimeUnit unit) throws InterruptedException;
//适用条件与行为与awaitNanos(long nanosTimeout)完全一样,唯一不同点在于它不是等待指定时间,而是等待由参数指定的某一时刻。
boolean awaitUntil(Date deadline) throws InterruptedException;
//唤醒一个在 await()等待队列中的线程。与Object.notify()相似
void signal();
//唤醒 await()等待队列中所有的线程。与object.notifyAll()相似
void signalAll();
}
import sun.awt.windows.ThemeReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class TestReentrantLockCondition<E> {
//面试题2
//固定同步容器 消费者和生产者
private LinkedList<E> list = new LinkedList<>();
private static final int MAX = 10;
private int count = 0;
private ReentrantLock reentrantLock = new ReentrantLock();
private Condition producer = reentrantLock.newCondition();
private Condition consumer = reentrantLock.newCondition();
public void put(E e) {
try {
reentrantLock.lock();
while (count == MAX) {
System.out.println(Thread.currentThread().getName() + " await()");
producer.await();
}
count++;
list.add(e);
System.out.println(Thread.currentThread().getName() + " add " + e);
consumer.signalAll();
} catch (InterruptedException e1) {
e1.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
public E get() {
E e = null;
try {
reentrantLock.lock();
while (count == 0) {
System.out.println(Thread.currentThread().getName() + " await()");
consumer.await();
}
count--;
e = list.removeFirst();
System.out.println(Thread.currentThread().getName() + " remove " + e);
producer.signalAll();
} catch (InterruptedException e2) {
e2.printStackTrace();
} finally {
reentrantLock.unlock();
}
return e;
}
public int getCount() {
return count;
}
public static void main(String[] args) {
TestReentrantLockCondition<String> testReentrantLockCondition = new TestReentrantLockCondition();
CountDownLatch countDownLatch = new CountDownLatch(20);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 5; j++) {
System.out.println(Thread.currentThread().getName() + " consume: " + testReentrantLockCondition.get());
}
countDownLatch.countDown();
}, "c" + i).start();
}
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 5; j++) {
testReentrantLockCondition.put(Thread.currentThread().getName() + " " + j);
}
countDownLatch.countDown();
}, "p" + i).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("mian end,count: " + testReentrantLockCondition.getCount());
}
}
全网很多这题的代码,但是大多数循环n次,可能出现1A2B3C的顺序,或者AB1C2之类的顺序错误。可以通过CountDownLatch控制哪个线程先执行。
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test_A1B2C3_ReentranLock_condition {
static volatile int i = 0;
public static void main(String[] args) {
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
for (int j = 0; j < 10000; j++) {
Lock lock = new ReentrantLock();
//控制哪个线程先执行
CountDownLatch count = new CountDownLatch(1);
//控制线程一起结束,类似于实现join()的功能,这个方式用起来比较简单
CountDownLatch countDownLatch = new CountDownLatch(14);
Condition condition = lock.newCondition();
new Thread(() -> {
try {
//数字线程先阻塞
count.await();
lock.lock();
for (char c : aI) {
System.out.print(c);
countDownLatch.countDown();
condition.signal();
condition.await();
}
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "t1").start();
new Thread(() -> {
try {
lock.lock();
//字母线程获取到锁之后,countDown(),保证字母线程先执行
count.countDown();
for (char c : aC) {
System.out.print(c);
countDownLatch.countDown();
condition.signal();
condition.await();
}
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "t2").start();
try {
//等待所有线程一起执行完,再执行后面流程(类似于join())
countDownLatch.await();
System.out.println();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}