ReadWriteLock是ReentrantReadWriteLock的接口,而ReentrantReadWriteLock实现类中包括子类ReadLock和WriteLock。
首先来看一下ReadWriteLock接口中方法的定义:
public interface ReadWriteLock {
Lock readLock(); // 返回用于读取操作的锁
Lock writeLock(); // 返回用于写入操作的锁
}
读取锁和写入锁不可以同时存储,且读取锁可以同时存在多个,但是写入锁只能存在一个。
来看实现类中部分变量和方法,如下:
- private final ReentrantReadWriteLock.ReadLock readerLock; // 读锁
- private final ReentrantReadWriteLock.WriteLock writerLock;// 写锁
- public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
- public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; }
- // 默认为非公平锁
- public ReentrantReadWriteLock() {
- this(false);
- }
- public ReentrantReadWriteLock(boolean fair) {
- sync = fair ? new FairSync() : new NonfairSync();
- readerLock = new ReadLock(this);
- writerLock = new WriteLock(this);
- }
private final ReentrantReadWriteLock.ReadLock readerLock; // 读锁
private final ReentrantReadWriteLock.WriteLock writerLock;// 写锁
public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; }
// 默认为非公平锁
public ReentrantReadWriteLock() {
this(false);
}
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
定义了读锁和写锁变量,同时提供了两个构造函数,用来构造公平或非公平锁。
1、获取共享读锁
ReadLock内部类的实现源代码如下:
- // 获取读锁,是共享锁
- public static class ReadLock implements Lock, java.io.Serializable {
- private final Sync sync;
- protected ReadLock(ReentrantReadWriteLock lock) {
- sync = lock.sync;
- }
- public void lock() { // 共享读锁的获取
- sync.acquireShared(1);
- }
- //Acquires the read lock unless the current thread is interrupted.
- public void lockInterruptibly() throws InterruptedException {
- sync.acquireSharedInterruptibly(1);
- }
- public boolean tryLock() {
- return sync.tryReadLock();
- }
- public boolean tryLock(long timeout, TimeUnit unit)
- throws InterruptedException {
- return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
- }
- public void unlock() {
- sync.releaseShared(1);
- }
- public Condition newCondition() {
- throw new UnsupportedOperationException();
- }
- public String toString() {
- int r = sync.getReadLockCount();
- return super.toString() +
- "[Read locks = " + r + "]";
- }
- }
// 获取读锁,是共享锁
public static class ReadLock implements Lock, java.io.Serializable {
private final Sync sync;
protected ReadLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
public void lock() { // 共享读锁的获取
sync.acquireShared(1);
}
//Acquires the read lock unless the current thread is interrupted.
public void lockInterruptibly() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public boolean tryLock() {
return sync.tryReadLock();
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
public void unlock() {
sync.releaseShared(1);
}
public Condition newCondition() {
throw new UnsupportedOperationException();
}
public String toString() {
int r = sync.getReadLockCount();
return super.toString() +
"[Read locks = " + r + "]";
}
}
读取锁是通过调用lock()方法来获取的,在这个方法中调用了acquireShared()方法,这个方法在AQS中实现,如下:
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
前面已经多次碰到这相同代码,不解释,直接来看tryAcquireShared()方法的实现:
- protected final int tryAcquireShared(int unused) {
- Thread current = Thread.currentThread();
- int c = getState(); // 获取锁的状态
- // 如果锁是互斥锁,并且获取锁的线程不是当前线程,则返回-1,表示获取失败
- if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current)
- return -1;
- int r = sharedCount(c); // 获取读取锁的共享计数
- // 如果不需要阻塞等待,并且读取锁的共享计数小于MAX_COUNT;
- // 则通过CAS函数更新锁的状态,将读取锁的共享计数+1。
- if (!readerShouldBlock() && r < MAX_COUNT && compareAndSetState(c, c + SHARED_UNIT)) {
- if (r == 0) { // 第1次获取读取锁
- firstReader = current;
- firstReaderHoldCount = 1;
- // 如果想要获取锁的线程(current)是第1个获取锁(firstReader)的线程
- } else if (firstReader == current) {
- firstReaderHoldCount++;
- } else {
- // HoldCounter是用来统计该线程获取读取锁的次数。
- HoldCounter rh = cachedHoldCounter;
- if (rh == null || rh.tid != current.getId())
- cachedHoldCounter = rh = readHolds.get();
- else if (rh.count == 0)
- readHolds.set(rh);
- rh.count++; // 将该线程获取读取锁的次数+1
- }
- return 1;
- }
- return fullTryAcquireShared(current);
- }
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState(); // 获取锁的状态
// 如果锁是互斥锁,并且获取锁的线程不是当前线程,则返回-1,表示获取失败
if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current)
return -1;
int r = sharedCount(c); // 获取读取锁的共享计数
// 如果不需要阻塞等待,并且读取锁的共享计数小于MAX_COUNT;
// 则通过CAS函数更新锁的状态,将读取锁的共享计数+1。
if (!readerShouldBlock() && r < MAX_COUNT && compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) { // 第1次获取读取锁
firstReader = current;
firstReaderHoldCount = 1;
// 如果想要获取锁的线程(current)是第1个获取锁(firstReader)的线程
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
// HoldCounter是用来统计该线程获取读取锁的次数。
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != current.getId())
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++; // 将该线程获取读取锁的次数+1
}
return 1;
}
return fullTryAcquireShared(current);
}
tryAcquireShared()的作用是尝试获取共享锁,如果通过如上的方法获取失败,则调用fullTryAcquireShared()方法来获取:
- final int fullTryAcquireShared(Thread current) {
- HoldCounter rh = null;
- for (;;) {
- int c = getState(); // 获取锁的状态
- if (exclusiveCount(c) != 0) { // 写线程获取互斥锁
- if (getExclusiveOwnerThread() != current) // 获取锁的线程不是当前线程
- return -1;
- } else if (readerShouldBlock()) { // 需要阻塞等待
- if (firstReader == current) { // 当前线程是第一个线程
- } else {
- if (rh == null) {
- rh = cachedHoldCounter;
- if (rh == null || rh.tid != current.getId()) {
- rh = readHolds.get();
- if (rh.count == 0)
- readHolds.remove();
- }
- }
- if (rh.count == 0)// 如果当前线程获取锁的计数为0,则返回-1。
- return -1;
- }
- }
- // 不需要阻塞等待,获取读取锁的共享统计数;如果共享统计数超过MAX_COUNT,则抛出异常
- if (sharedCount(c) == MAX_COUNT)
- throw new Error("Maximum lock count exceeded");
- // 将线程获取读取锁的次数加1。
- if (compareAndSetState(c, c + SHARED_UNIT)) {
- // 如果是第1次获取“读取锁”,则更新firstReader和firstReaderHoldCount。
- if (sharedCount(c) == 0) {
- firstReader = current;
- firstReaderHoldCount = 1;
- // 如果想要获取锁的线程(current)是第1个获取锁(firstReader)的线程,
- // 则将firstReaderHoldCount+1。
- } else if (firstReader == current) {
- firstReaderHoldCount++;
- } else {
- if (rh == null)
- rh = cachedHoldCounter;
- if (rh == null || rh.tid != current.getId())
- rh = readHolds.get();
- else if (rh.count == 0)
- readHolds.set(rh);
- // 更新线程的获取“读取锁”的共享计数
- rh.count++;
- cachedHoldCounter = rh; // cache for release
- }
- return 1;
- }
- }
- }
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
for (;;) {
int c = getState(); // 获取锁的状态
if (exclusiveCount(c) != 0) { // 写线程获取互斥锁
if (getExclusiveOwnerThread() != current) // 获取锁的线程不是当前线程
return -1;
} else if (readerShouldBlock()) { // 需要阻塞等待
if (firstReader == current) { // 当前线程是第一个线程
} else {
if (rh == null) {
rh = cachedHoldCounter;
if (rh == null || rh.tid != current.getId()) {
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
}
}
if (rh.count == 0)// 如果当前线程获取锁的计数为0,则返回-1。
return -1;
}
}
// 不需要阻塞等待,获取读取锁的共享统计数;如果共享统计数超过MAX_COUNT,则抛出异常
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// 将线程获取读取锁的次数加1。
if (compareAndSetState(c, c + SHARED_UNIT)) {
// 如果是第1次获取“读取锁”,则更新firstReader和firstReaderHoldCount。
if (sharedCount(c) == 0) {
firstReader = current;
firstReaderHoldCount = 1;
// 如果想要获取锁的线程(current)是第1个获取锁(firstReader)的线程,
// 则将firstReaderHoldCount+1。
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
if (rh == null)
rh = cachedHoldCounter;
if (rh == null || rh.tid != current.getId())
rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
// 更新线程的获取“读取锁”的共享计数
rh.count++;
cachedHoldCounter = rh; // cache for release
}
return 1;
}
}
}
doAcquireShared()定义在AQS函数中,源码如下:
- private void doAcquireShared(int arg) {
- final Node node = addWaiter(Node.SHARED);
- boolean failed = true;
- try {
- boolean interrupted = false;
- for (;;) {
- final Node p = node.predecessor();
- if (p == head) {
- int r = tryAcquireShared(arg);
- if (r >= 0) {
- setHeadAndPropagate(node, r);
- p.next = null; // help GC
- if (interrupted)
- selfInterrupt();
- failed = false;
- return;
- }
- }
- if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
- interrupted = true;
- }
- } finally {
- if (failed)
- cancelAcquire(node);
- }
- }
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
这段代码与前面的实现相同,在此不估过多的解释。
2、释放共享读锁
调用ReadLock中的unlock()方法来释放共享读锁,在这个方法中调用了如下的方法:
- public final boolean releaseShared(int arg) {
- if (tryReleaseShared(arg)) {
- doReleaseShared();
- return true;
- }
- return false;
- }
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
- protected final boolean tryReleaseShared(int unused) {
- // 获取当前线程,即释放共享锁的线程
- Thread current = Thread.currentThread();
- // 如果想要释放锁的线程(current)是第1个获取锁(firstReader)的线程,
- // 并且第1个获取锁的线程获取锁的次数=1,则设置firstReader为null;
- // 否则,将第1个获取锁的线程的获取次数-1。
- if (firstReader == current) {
- // assert firstReaderHoldCount > 0;
- if (firstReaderHoldCount == 1)
- firstReader = null;
- else
- firstReaderHoldCount--;
- } else {
- // 获取rh对象,并更新当前线程获取锁的信息
- HoldCounter rh = cachedHoldCounter;
- if (rh == null || rh.tid != current.getId())
- rh = readHolds.get();
- int count = rh.count;
- if (count <= 1) {
- readHolds.remove();
- if (count <= 0)
- throw unmatchedUnlockException();
- }
- --rh.count;
- }
- for (;;) {
- int c = getState(); // 获取锁的状态
- int nextc = c - SHARED_UNIT;// 将锁的获取次数-1
- // 通过CAS更新锁的状态
- if (compareAndSetState(c, nextc))
- return nextc == 0;
- }
- }
protected final boolean tryReleaseShared(int unused) {
// 获取当前线程,即释放共享锁的线程
Thread current = Thread.currentThread();
// 如果想要释放锁的线程(current)是第1个获取锁(firstReader)的线程,
// 并且第1个获取锁的线程获取锁的次数=1,则设置firstReader为null;
// 否则,将第1个获取锁的线程的获取次数-1。
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {
// 获取rh对象,并更新当前线程获取锁的信息
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != current.getId())
rh = readHolds.get();
int count = rh.count;
if (count <= 1) {
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
--rh.count;
}
for (;;) {
int c = getState(); // 获取锁的状态
int nextc = c - SHARED_UNIT;// 将锁的获取次数-1
// 通过CAS更新锁的状态
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
- private void doReleaseShared() {
- for (;;) {
- // 获取CLH队列的头节点
- Node h = head;
- // 如果头节点不为null,并且头节点不等于tail节点。
- if (h != null && h != tail) {
- // 获取头节点对应的线程的状态
- int ws = h.waitStatus;
- // 如果头节点对应的线程是SIGNAL状态,则意味着“头节点的下一个节点所对应的线程”需要被unpark唤醒。
- if (ws == Node.SIGNAL) {
- // 设置“头节点对应的线程状态”为空状态。失败的话,则继续循环。
- if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
- continue;
- // 唤醒“头节点的下一个节点所对应的线程”。
- unparkSuccessor(h);
- }
- // 如果头节点对应的线程是空状态,则设置“文件点对应的线程所拥有的共享锁”为其它线程获取锁的空状态。
- else if (ws == 0 &&
- !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
- continue; // loop on failed CAS
- }
- // 如果头节点发生变化,则继续循环。否则,退出循环。
- if (h == head) // loop if head changed
- break;
- }
- }
private void doReleaseShared() {
for (;;) {
// 获取CLH队列的头节点
Node h = head;
// 如果头节点不为null,并且头节点不等于tail节点。
if (h != null && h != tail) {
// 获取头节点对应的线程的状态
int ws = h.waitStatus;
// 如果头节点对应的线程是SIGNAL状态,则意味着“头节点的下一个节点所对应的线程”需要被unpark唤醒。
if (ws == Node.SIGNAL) {
// 设置“头节点对应的线程状态”为空状态。失败的话,则继续循环。
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue;
// 唤醒“头节点的下一个节点所对应的线程”。
unparkSuccessor(h);
}
// 如果头节点对应的线程是空状态,则设置“文件点对应的线程所拥有的共享锁”为其它线程获取锁的空状态。
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
// 如果头节点发生变化,则继续循环。否则,退出循环。
if (h == head) // loop if head changed
break;
}
}
3、读取锁的公正锁和非公平锁
公平锁和非公平锁的区别,体现在判断是否需要阻塞的函数readerShouldBlock()是不同的。公平锁的readerShouldBlock()的源码如下:
final boolean readerShouldBlock() {
return hasQueuedPredecessors();
}
在公平共享锁中,如果在当前线程的前面有其他线程在等待获取共享锁,则返回true;否则,返回false。
非公平锁的readerShouldBlock()的源码如下:
final boolean readerShouldBlock() {
return apparentlyFirstQueuedIsExclusive();
}
在非公平共享锁中,它会无视当前线程的前面是否有其他线程在等待获取共享锁。只要该非公平共享锁对应的线程不为null,则返回true
4、举例
- public class ReadWriteLockTest1 {
- public static void main(String[] args) {
- // 创建账户
- MyCount myCount = new MyCount("4238920615242830", 10000);
- // 创建用户,并指定账户
- User user = new User("Tommy", myCount);
- // 分别启动3个“读取账户金钱”的线程 和 3个“设置账户金钱”的线程
- for (int i=0; i<3; i++) {
- user.getCash();
- user.setCash((i+1)*1000);
- }
- }
- }
- class User {
- private String name; //用户名
- private MyCount myCount; //所要操作的账户
- private ReadWriteLock myLock; //执行操作所需的锁对象
- User(String name, MyCount myCount) {
- this.name = name;
- this.myCount = myCount;
- this.myLock = new ReentrantReadWriteLock();
- }
- public void getCash() {
- new Thread() {
- public void run() {
- myLock.readLock().lock();
- try {
- System.out.println(Thread.currentThread().getName() +" getCash start");
- myCount.getCash();
- Thread.sleep(1);
- System.out.println(Thread.currentThread().getName() +" getCash end");
- } catch (InterruptedException e) {
- } finally {
- myLock.readLock().unlock();
- }
- }
- }.start();
- }
- public void setCash(final int cash) {
- new Thread() {
- public void run() {
- myLock.writeLock().lock();
- try {
- System.out.println(Thread.currentThread().getName() +" setCash start");
- myCount.setCash(cash);
- Thread.sleep(1);
- System.out.println(Thread.currentThread().getName() +" setCash end");
- } catch (InterruptedException e) {
- } finally {
- myLock.writeLock().unlock();
- }
- }
- }.start();
- }
- }
- class MyCount {
- private String id; //账号
- private int cash; //账户余额
- MyCount(String id, int cash) {
- this.id = id;
- this.cash = cash;
- }
- public String getId() {
- return id;
- }
- public void setId(String id) {
- this.id = id;
- }
- public int getCash() {
- System.out.println(Thread.currentThread().getName() +" getCash cash="+ cash);
- return cash;
- }
- public void setCash(int cash) {
- System.out.println(Thread.currentThread().getName() +" setCash cash="+ cash);
- this.cash = cash;
- }
- }
public class ReadWriteLockTest1 {
public static void main(String[] args) {
// 创建账户
MyCount myCount = new MyCount("4238920615242830", 10000);
// 创建用户,并指定账户
User user = new User("Tommy", myCount);
// 分别启动3个“读取账户金钱”的线程 和 3个“设置账户金钱”的线程
for (int i=0; i<3; i++) {
user.getCash();
user.setCash((i+1)*1000);
}
}
}
class User {
private String name; //用户名
private MyCount myCount; //所要操作的账户
private ReadWriteLock myLock; //执行操作所需的锁对象
User(String name, MyCount myCount) {
this.name = name;
this.myCount = myCount;
this.myLock = new ReentrantReadWriteLock();
}
public void getCash() {
new Thread() {
public void run() {
myLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() +" getCash start");
myCount.getCash();
Thread.sleep(1);
System.out.println(Thread.currentThread().getName() +" getCash end");
} catch (InterruptedException e) {
} finally {
myLock.readLock().unlock();
}
}
}.start();
}
public void setCash(final int cash) {
new Thread() {
public void run() {
myLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() +" setCash start");
myCount.setCash(cash);
Thread.sleep(1);
System.out.println(Thread.currentThread().getName() +" setCash end");
} catch (InterruptedException e) {
} finally {
myLock.writeLock().unlock();
}
}
}.start();
}
}
class MyCount {
private String id; //账号
private int cash; //账户余额
MyCount(String id, int cash) {
this.id = id;
this.cash = cash;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getCash() {
System.out.println(Thread.currentThread().getName() +" getCash cash="+ cash);
return cash;
}
public void setCash(int cash) {
System.out.println(Thread.currentThread().getName() +" setCash cash="+ cash);
this.cash = cash;
}
}
运行的结果如下:
- Thread-0 getCash start
- Thread-2 getCash start
- Thread-0 getCash cash=10000
- Thread-2 getCash cash=10000
- Thread-0 getCash end
- Thread-2 getCash end
- Thread-1 setCash start
- Thread-1 setCash cash=1000
- Thread-1 setCash end
- Thread-3 setCash start
- Thread-3 setCash cash=2000
- Thread-3 setCash end
- Thread-4 getCash start
- Thread-4 getCash cash=2000
- Thread-4 getCash end
- Thread-5 setCash start
- Thread-5 setCash cash=3000
- Thread-5 setCash end
Thread-0 getCash start
Thread-2 getCash start
Thread-0 getCash cash=10000
Thread-2 getCash cash=10000
Thread-0 getCash end
Thread-2 getCash end
Thread-1 setCash start
Thread-1 setCash cash=1000
Thread-1 setCash end
Thread-3 setCash start
Thread-3 setCash cash=2000
Thread-3 setCash end
Thread-4 getCash start
Thread-4 getCash cash=2000
Thread-4 getCash end
Thread-5 setCash start
Thread-5 setCash cash=3000
Thread-5 setCash end
转载自http://blog.youkuaiyun.com/mazhimazh/