摘要:本文主要讲了读写锁。
一、读写锁ReadWriteLock
在上文中回顾了并发包中的可重入锁ReentrantLock,并且也分析了它的源码。从中我们知道它是一个单一锁(笔者自创概念),意思是在多人读、多人写、或同时有人读和写时。只能有一个人能拿到锁,执行代码。但是在很多场景。我们想控制它能多人同时读,但是又不让它多人写或同时读和写时。(想想这是不是和数据库的可重复读有点类型?),这时就可以使用读写锁:ReadWriteLock。
下面来看一个应用
- package com.lin;
-
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import java.util.concurrent.locks.ReadWriteLock;
- import java.util.concurrent.locks.ReentrantReadWriteLock;
-
- public class ReadWriteLockTest {
-
- public static void main(String[] args) {
-
- ReadWriteLock lock = new ReentrantReadWriteLock(false);
-
- ExecutorService pool = Executors.newCachedThreadPool();
-
- Account account = new Account(lock,"123456",10000);
-
-
- for(int i=1;i<=10;i++) {
- Operation operation1 = new Operation(account,"take");
- Operation operation2 = new Operation(account,"query");
- Operation operation3 = new Operation(account,"save");
- Operation operation4 = new Operation(account,"query");
- pool.execute(operation1);
- pool.execute(operation2);
- pool.execute(operation3);
- pool.execute(operation4);
- }
- pool.shutdown();
- while(!pool.isTerminated()){
-
- }
- System.out.println("账号"+ account.getAccoutNo() +",最后金额为:"+account.getMoney());
- }
-
- }
-
- class Operation implements Runnable{
-
- private Account account;
-
- private String type;
-
- Operation(Account account,String type){
- this.account = account;
- this.type = type;
- }
-
-
- public void run() {
- if ("take".equals(type)) {
-
- account.getLock().writeLock().lock();
- account.setMoney(account.getMoney() -100);
- System.out.println( "取走100元,账号"+ account.getAccoutNo()+" 还有"+account.getMoney()+"元");
- account.getLock().writeLock().unlock();
-
- }
- else if ("query".equals(type)) {
-
- account.getLock().readLock().lock();
- System.out.println( "查询账号"+ account.getAccoutNo()+" 还有"+account.getMoney()+"元");
- account.getLock().readLock().unlock();
-
- }
- else if ("save".equals(type)) {
-
- account.getLock().writeLock().lock();
- account.setMoney(account.getMoney() + 100);
- System.out.println( "存入100元,账号"+ account.getAccoutNo()+" 还有"+account.getMoney()+"元");
- account.getLock().writeLock().unlock();
- }
- }
-
- }
-
- class Account {
-
- private int money;
-
- private ReadWriteLock lock;
-
- private String accoutNo;
-
- Account(ReadWriteLock lock,String accoutNo,int money) {
- this.lock = lock;
- this.accoutNo = accoutNo;
- this.money = money;
- }
-
- public int getMoney() {
- return money;
- }
-
- public void setMoney(int money) {
- this.money = money;
- }
-
- public ReadWriteLock getLock() {
- return lock;
- }
-
- public void setLock(ReadWriteLock lock) {
- this.lock = lock;
- }
-
- public String getAccoutNo() {
- return accoutNo;
- }
-
- public void setAccoutNo(String accoutNo) {
- this.accoutNo = accoutNo;
- }
-
- }
输出结果:
在上面的例子中,设置了一个账号。金额为10000,然后开了10条线程每次取100,10条线程每次存100,20条线程一直查。从结果中我们可以看到是正确的。
二、源码分析
1、ReadWriteLock
- public interface ReadWriteLock {
-
- Lock readLock();
-
- Lock writeLock();
- }
ReadWriteLock就只是一个接口类,真正实现 类在ReentrantReadWriteLock
2、ReentrantReadWriteLock
(1)包含变量
- public class ReentrantReadWriteLock
- implements ReadWriteLock, java.io.Serializable {
- private static final long serialVersionUID = -6992448646407690164L;
-
- private final ReentrantReadWriteLock.ReadLock readerLock;
-
- private final ReentrantReadWriteLock.WriteLock writerLock;
-
- final Sync sync;
看了一个它的变量还是比较简单的,其中Sync类在ReentrantLock类中笔者已介绍过。
(2)构造函数
- public ReentrantReadWriteLock() {
- this(false);
- }
-
-
-
-
- public ReentrantReadWriteLock(boolean fair) {
- sync = fair ? new FairSync() : new NonfairSync();
- readerLock = new ReadLock(this);
- writerLock = new WriteLock(this);
- }
看了看FairSync的源码其实和上节中讲的基本一样。这里就不再展开。
(3)ReadLock
看了下读锁,其实很简单,发现里面封装的都 是调用Sync类的方法,看来它才是重点。在ReadLock类的lock方法中,我们看到了sync.acquireShared(1);这里就可以认为是一个共享锁
- public static class ReadLock implements Lock, java.io.Serializable {
- private static final long serialVersionUID = -5992448646407690164L;
- private final Sync sync;
- protected ReadLock(ReentrantReadWriteLock lock) {
- sync = lock.sync;
- }
-
- public void lock() {
- sync.acquireShared(1);
- }
-
-
- 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 final void acquireShared(int arg) {
- if (tryAcquireShared(arg) < 0)
- doAcquireShared(arg);
- }
再进去就看不了,这里的方法其实就是认为取得一个共享锁。
(4)WriteLock
- public static class WriteLock implements Lock, java.io.Serializable {
- private static final long serialVersionUID = -4992448646407690164L;
- private final Sync sync;
-
- protected WriteLock(ReentrantReadWriteLock lock) {
- sync = lock.sync;
- }
-
- public void lock() {
- sync.acquire(1);
- }
-
- public void lockInterruptibly() throws InterruptedException {
- sync.acquireInterruptibly(1);
- }
在WriteLock锁中的lock方法和ReadLock是有所不同的。WriteLock它是一个独占锁。也就是有线程拿到后,其它线程就得阻塞等待了。
- public final void acquire(int arg) {
- if (!tryAcquire(arg) &&
- acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
- selfInterrupt();
- }