ReadWriteLock管理一组锁,一个是只读的锁,一个是写锁。读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的。
所有读写锁的实现必须确保写操作对读操作的内存影响。
非公平模式(默认)
当以非公平初始化时,读锁和写锁的获取的顺序是不确定的。非公平锁主张竞争获取,可能会延缓一个或多个读或写线程,但是会比公平锁有更高的吞吐量。
公平模式
当以公平模式初始化时,线程将会以队列的顺序获取锁。当当前线程释放锁后,等待时间最长的写锁线程就会被分配写锁;或者有一组读线程组等待时间比写线程长,那么这组读线程组将会被分配读锁。
当有写线程持有写锁或者有等待的写线程时,一个尝试获取公平的读锁(非重入)的线程就会阻塞。这个线程直到等待时间最长的写锁获得锁后并释放掉锁后才能获取到读锁。
公平锁和非公平锁的区别
公平锁:当线程发现已经有线程在排对获取锁了,那么它必须排队,除了一种情况就是,线程已经占有锁,此次是重入,不用排队。
非公平锁:只有一种情况需排队,其他情况不用排队就可以尝试获取锁: 如果当前全局处于读锁状态,且等待队列中第一个等待线程想获取写锁,那么当前线程能够获取到读锁的条件为:当前线程获取了写锁,还未释放;当前线程获取了读锁,这一次只是重入读锁而已;其它情况当前线程入队尾。
模拟一个列子,多个人同时操作一个银行账户查询或者存取操作
1.账号
public class MyCount {
private String cardNumber;//卡号
private BigDecimal balance;//余额
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "MyCount [cardNumber=" + cardNumber + ", balance=" + balance + "]";
}
/**
* @return the cardNumber
*/
public String getCardNumber() {
return cardNumber;
}
/**
* @param cardNumber the cardNumber to set
*/
public void setCardNumber(String cardNumber) {
this.cardNumber = cardNumber;
}
/**
* @return the balance
*/
public BigDecimal getBalance() {
return balance;
}
/**
* @param balance the balance to set
*/
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
}
2. 用户查询和存取钱操作
@Slf4j
public class BankTask implements Callable {
private String name; //用户名
private MyCount myCount; //所要操作的账户
private BigDecimal cash; //操作的金额
private ReadWriteLock myLock; //执行操作所需的锁对象
private boolean ischeck; //是否查询
public BankTask(String name, MyCount myCount, BigDecimal cash, ReadWriteLock myLock, boolean ischeck) {
super();
this.name = name;
this.myCount = myCount;
this.cash = cash;
this.myLock = myLock;
this.ischeck = ischeck;
}
@Override
public Object call() throws Exception {
Map<String, Object> map = new HashMap<String, Object>();
if (ischeck) {
myLock.readLock().lock();
log.info("用户" + name + "正在查询" + myCount + "账户,当前金额为 :" + myCount.getBalance());
myLock.readLock().unlock();
} else {
myLock.writeLock().lock();
log.info("用户操作前卡中信息 :" + name + "正在查询" + myCount + "账户,当前金额为 :" + myCount.getBalance());
String balance = myCount.getBalance().toString();
//假如为取钱业务,需要判断当前账户余额是否充足
BigDecimal drawMoney = myCount.getBalance().add(cash);
int compare = drawMoney.compareTo(BigDecimal.ZERO);
if (compare == Constant.NEGATIVE) {//余额不足
map.put("CODE", Constant.NEGATIVE);
map.put("MSG", "FAIL : not sufficient funds .");
map.put("SUCCESS", false);
} else {
myCount.setBalance(myCount.getBalance().add(cash));
log.info("操作银行卡(存钱/取钱) :¥" + cash + "元 , 操作前余额为 :$" + balance + "元 ," + name + "正在查询" + myCount + "账户,当前金额为 :" + myCount.getBalance());
myLock.writeLock().unlock();
map.put("CODE", 0);
map.put("MSG", "SUCCESS");
map.put("SUCCESS", true);
}
}
return map;
}
}
3. 模拟多用户操作
@Slf4j
public class ReentrantReadWriteLockTest {
@SuppressWarnings({ "unchecked", "rawtypes" })
public static void main(String[] args) throws InterruptedException, ExecutionException {
MyCount myCount = new MyCount();
myCount.setBalance(new BigDecimal(10000));//账户余额
myCount.setCardNumber("100086110120119");
ReadWriteLock lock = new ReentrantReadWriteLock(false);//查询余额参数为true 存取操作参数为false
ExecutorService pool = Executors.newCachedThreadPool();
String[] name = {"张三","张三的父亲","张三的母亲","张三的大儿子","张三的二儿子","张三的三儿子","张三的四儿子","张三的五儿子","张三的六儿子","张三的七儿子"};
String[] operateBalance = {"-20000","8000","-1000","500","500","-1000","1000","2000","3000","-10000"};
for (int i = 0; i < name.length; i++) {
BankTask bankTask = new BankTask(name[i], myCount, new BigDecimal(operateBalance[i]), lock, false);
Future future = pool.submit(bankTask);
Map<String, Object> returnMap = (Map<String, Object>) future.get();
log.info("Thread " + i + ": " + name[i] + "操作结果 result : { CODE :" + returnMap.get("CODE") + " MSG :" + returnMap.get("MSG") + " SUCCESS :" + returnMap.get("SUCCESS"));
}
pool.shutdown();
}
}
输出结果:
- 用户操作前卡中信息 :张三正在查询MyCount [cardNumber=100086110120119, balance=10000]账户,当前金额为 :10000
- Thread 0: 张三操作结果 result : { CODE :-1 MSG :FAIL : not sufficient funds . SUCCESS :false
- 用户操作前卡中信息 :张三的父亲正在查询MyCount [cardNumber=100086110120119, balance=10000]账户,当前金额为 :10000
- 操作银行卡(存钱/取钱) :¥8000元 , 操作前余额为 :$10000元 ,张三的父亲正在查询MyCount [cardNumber=100086110120119, balance=18000]账户,当前金额为 :18000
- Thread 1: 张三的父亲操作结果 result : { CODE :0 MSG :SUCCESS SUCCESS :true
- 用户操作前卡中信息 :张三的母亲正在查询MyCount [cardNumber=100086110120119, balance=18000]账户,当前金额为 :18000
- 操作银行卡(存钱/取钱) :¥-1000元 , 操作前余额为 :$18000元 ,张三的母亲正在查询MyCount [cardNumber=100086110120119, balance=17000]账户,当前金额为 :17000
- Thread 2: 张三的母亲操作结果 result : { CODE :0 MSG :SUCCESS SUCCESS :true
- 用户操作前卡中信息 :张三的大儿子正在查询MyCount [cardNumber=100086110120119, balance=17000]账户,当前金额为 :17000
- 操作银行卡(存钱/取钱) :¥500元 , 操作前余额为 :$17000元 ,张三的大儿子正在查询MyCount [cardNumber=100086110120119, balance=17500]账户,当前金额为 :17500
- Thread 3: 张三的大儿子操作结果 result : { CODE :0 MSG :SUCCESS SUCCESS :true
- 用户操作前卡中信息 :张三的二儿子正在查询MyCount [cardNumber=100086110120119, balance=17500]账户,当前金额为 :17500
- 操作银行卡(存钱/取钱) :¥500元 , 操作前余额为 :$17500元 ,张三的二儿子正在查询MyCount [cardNumber=100086110120119, balance=18000]账户,当前金额为 :18000
- Thread 4: 张三的二儿子操作结果 result : { CODE :0 MSG :SUCCESS SUCCESS :true
- 用户操作前卡中信息 :张三的三儿子正在查询MyCount [cardNumber=100086110120119, balance=18000]账户,当前金额为 :18000
- 操作银行卡(存钱/取钱) :¥-1000元 , 操作前余额为 :$18000元 ,张三的三儿子正在查询MyCount [cardNumber=100086110120119, balance=17000]账户,当前金额为 :17000
- Thread 5: 张三的三儿子操作结果 result : { CODE :0 MSG :SUCCESS SUCCESS :true
- 用户操作前卡中信息 :张三的四儿子正在查询MyCount [cardNumber=100086110120119, balance=17000]账户,当前金额为 :17000
- 操作银行卡(存钱/取钱) :¥1000元 , 操作前余额为 :$17000元 ,张三的四儿子正在查询MyCount [cardNumber=100086110120119, balance=18000]账户,当前金额为 :18000
- Thread 6: 张三的四儿子操作结果 result : { CODE :0 MSG :SUCCESS SUCCESS :true
- 用户操作前卡中信息 :张三的五儿子正在查询MyCount [cardNumber=100086110120119, balance=18000]账户,当前金额为 :18000
- 操作银行卡(存钱/取钱) :¥2000元 , 操作前余额为 :$18000元 ,张三的五儿子正在查询MyCount [cardNumber=100086110120119, balance=20000]账户,当前金额为 :20000
- Thread 7: 张三的五儿子操作结果 result : { CODE :0 MSG :SUCCESS SUCCESS :true
- 用户操作前卡中信息 :张三的六儿子正在查询MyCount [cardNumber=100086110120119, balance=20000]账户,当前金额为 :20000
- 操作银行卡(存钱/取钱) :¥3000元 , 操作前余额为 :$20000元 ,张三的六儿子正在查询MyCount [cardNumber=100086110120119, balance=23000]账户,当前金额为 :23000
- Thread 8: 张三的六儿子操作结果 result : { CODE :0 MSG :SUCCESS SUCCESS :true
- 用户操作前卡中信息 :张三的七儿子正在查询MyCount [cardNumber=100086110120119, balance=23000]账户,当前金额为 :23000
- 操作银行卡(存钱/取钱) :¥-10000元 , 操作前余额为 :$23000元 ,张三的七儿子正在查询MyCount [cardNumber=100086110120119, balance=13000]账户,当前金额为 :13000
- Thread 9: 张三的七儿子操作结果 result : { CODE :0 MSG :SUCCESS SUCCESS :true