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);
}
定义了读锁和写锁变量,同时提供了两个构造函数,用来构造公平或非公平锁。
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 + "]";
}
}
读取锁是通过调用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);
}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;
}
}
}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);
}
}这段代码与前面的实现相同,在此不估过多的解释。2、释放共享读锁
调用ReadLock中的unlock()方法来释放共享读锁,在这个方法中调用了如下的方法:
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;
}
}
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;
}
}公平锁和非公平锁的区别,体现在判断是否需要阻塞的函数readerShouldBlock()是不同的。公平锁的readerShouldBlock()的源码如下:
final boolean readerShouldBlock() {
return hasQueuedPredecessors();
}在公平共享锁中,如果在当前线程的前面有其他线程在等待获取共享锁,则返回true;否则,返回false。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;
}
}运行的结果如下:
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.csdn.net/mazhimazh/article/details/19187839