ReentrantReadWriteLock 实现 interface ReadWriteLock:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public interface ReadWriteLock { /** * Returns the lock used for reading. * * @return the lock used for reading */ Lock readLock(); /** * Returns the lock used for writing. * * @return the lock used for writing */ Lock writeLock(); } |
所以 ReentrantReadWriteLock 并非是真的一个“锁”,而是一个读锁和写锁的管理器,而真正实现锁功能的是 ReentrantReadWriteLock 的静态内部类 ReadLock 和 WriteLock。ReadLock 和 WriteLock 都是直接实现 interface Lock。
当多线程共享同一个数据源时,为了避免数据上的错乱,我们需要在读数据的时候防止写入操作,在写入的时候也不能进行读操作。怎么实现这一业务?synchronized、ReentrantLock 都可以实现,在读、写操作的方法都加上锁的保护,则可实现读写互斥。但这种做法会使并发大大折扣,例如在某一时间段只有大量读操作并没有写操作,但此时读操作也要进行排队,此时 ReentrantReadWriteLock 的价值就能发挥得淋漓尽致。
ReentrantReadWriteLock 的作用:支持同时多读,读的时候不能写,写的时候不能读。
看看具体的 DEMO1,一个支持多用户的银行账号(夫妻共用)的存款和查询的需求:
1,可多用户同时查询余额(读操作)
2,当有用户在查询余额时(读操作),该账号不能进行存取款操作(写入操作)
3,当有用户在存取款时(写入操作),该账号不能进行查询余额操作(读操作)
具体实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | public class Test { static class User { int mID; BankAccount mBankAccount; public User(int ID, BankAccount bankAccount) { mID = ID; mBankAccount = bankAccount; } public void saveMoney(final int cash) { new Thread(new Runnable() { @Override public void run() { mBankAccount.saveMoney(mID, cash); } }).start(); } public void checkBalance() { new Thread(new Runnable() { @Override public void run() { mBankAccount.checkBalance(mID); } }).start(); } } /** * 支持多用户的银行账号(夫妻共用) */ static class BankAccount { int mBalance; ReentrantReadWriteLock mReentrantReadWriteLock = new ReentrantReadWriteLock(); ReentrantReadWriteLock.WriteLock mWriteLock = mReentrantReadWriteLock.writeLock(); ReentrantReadWriteLock.ReadLock mReadLock = mReentrantReadWriteLock.readLock(); public void saveMoney(int id, int cash) { mWriteLock.lock(); try { mBalance = mBalance + cash; System.out.println(" 用户 " + id + " 正在进行存钱操作 "); Thread.sleep(2000); System.out.println(" 用户 " + id + " 存钱完成,存入 " + cash + " 元 "); } catch (Exception e) { e.printStackTrace(); } finally { mWriteLock.unlock(); } } public void checkBalance(int id) { mReadLock.lock(); try { System.out.println(" 用户 " + id + " 正在进行查询余额操作 "); Thread.sleep(2000); System.out.println(" 余额:" + mBalance); } catch (Exception e) { e.printStackTrace(); } finally { mReadLock.unlock(); } } } public static void main(String[] var0) { BankAccount bankAccount = new BankAccount(); User user1 = new User(1, bankAccount); User user2 = new User(2, bankAccount); user1.saveMoney(1000000); // 同时查询余额 user1.checkBalance(); user2.checkBalance(); // 同时存款 user1.saveMoney(2000000); user2.saveMoney(6000); user1.checkBalance(); } } |
DEMO1 的输出:
1 2 3 4 5 6 7 8 9 10 11 12 | 用户 1 正在进行存钱操作 用户 1 存钱完成,存入 1000000 元 用户 1 正在进行查询余额操作 用户 2 正在进行查询余额操作 余额:1000000 余额:1000000 用户 1 正在进行存钱操作 用户 1 存钱完成,存入 2000000 元 用户 2 正在进行存钱操作 用户 2 存钱完成,存入 6000 元 用户 1 正在进行查询余额操作 余额:3006000 |
当两个用户同时查询余额时,可同时进行。
当两个用户同时存款时,不能同时进行,要进行队列排队。
可见,我们可以用 ReentrantReadWriteLock 进行一些读写需要互斥的一些业务上,特别适合在“大量读小量写”的业务,可以增大吞吐率。