在上文中提到了Lock接口以及对象,使用它,很优雅的控制了竞争资源的安全访问,但是这种锁不区分读写,称这种锁为普通锁。为了提高性能,Java提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制,在一定程度上提高了程序的执行效率。
Java中读写锁有个接口java.util.concurrent.locks.ReadWriteLock,也有具体的实现ReentrantReadWriteLock,详细的API可以查看JavaAPI文档。
下面这个例子是在文例子的基础上,将普通锁改为读写锁,并添加账户余额查询的功能,代码如下:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
//线程锁对象
public class WriteReadLockTest {
public static void main(String[] args) {
WriteReadLockTest wrt = new WriteReadLockTest();
// 创建并发访问的账户 来个一个亿过把有钱人的瘾
MyAccount myAcc = wrt.new MyAccount("95599200901215522", 100000000);
// 创建一个对象锁
ReentrantReadWriteLock lock = new ReentrantReadWriteLock(false);
// 创建一个线程池
ExecutorService pl = Executors.newFixedThreadPool(2);
// 创建并发用户 一张信用卡,存的存,取的取,好热闹啊
User u1 = wrt.new User("王五", myAcc, 100000, lock, false);
User u2 = wrt.new User("王五他爹", myAcc, 0, lock, true);
User u3 = wrt.new User("王五他弟", myAcc, -400000, lock, false);
User u4 = wrt.new User("王五他妹", myAcc, 100000, lock, false);
User u5 = wrt.new User("王五", myAcc, 0, lock, true);// 查询
User u6 = wrt.new User("王五", myAcc, 100000, lock, false);
pl.execute(u1);
pl.execute(u2);
pl.execute(u3);
pl.execute(u4);
pl.execute(u5);
pl.execute(u6);
pl.shutdown();
}
class User implements Runnable {
private String name;
private MyAccount myAccount;
private int opcash;// 操作金额
private ReadWriteLock myLock;// 锁对象
private boolean isCheck;// 是否查询
public User(String name, MyAccount myAccount, int opcash,
ReadWriteLock myLock, boolean isCheck) {
this.name = name;
this.myAccount = myAccount;
this.opcash = opcash;
this.myLock = myLock;
this.isCheck = isCheck;
}
@Override
public void run() {
String op;
if (opcash > 0) {
op = "存款";
} else if (opcash < 0) {
op = "取款";
} else {
op = "查询";
}
if (isCheck) {
// 获取读锁
myLock.readLock().lock();
System.err.println("读:" + name + "正在操作" + myAccount + "账户,"
+ op + "当前金额为" + myAccount.getCash());
// 释放读锁
myLock.readLock().unlock();
} else {
// 获取写锁
myLock.writeLock().lock();
// 执行现金业务
System.out.println("写:" + name + "正在操作" + myAccount + "账户,"
+ op + "金额为" + opcash + ",当前金额为" + myAccount.getCash());
myAccount.setCash(myAccount.getCash() + opcash);
System.out.println("写:" + name + "操作" + myAccount + "账户成功,"
+ op + "金额为" + opcash + ",当前金额为" + myAccount.getCash());
// 释放写锁,否则别的线程没有机会执行了
myLock.writeLock().unlock();
}
}
}
// 信用卡,可随意透支
class MyAccount {
private String cardno;// 卡号
private int cash;// 余额 测试就直接用int了
public MyAccount(String cardno, int cash) {
super();
this.cardno = cardno;
this.cash = cash;
}
public String getCardno() {
return cardno;
}
public void setCardno(String cardno) {
this.cardno = cardno;
}
public int getCash() {
return cash;
}
public void setCash(int cash) {
this.cash = cash;
}
@Override
public String toString() {
return "MyAccount{" + "卡号='" + cardno + '\'' + ", 余额=" + cash + '}';
}
}
}
运行结果:
写:王五正在操作MyAccount{卡号='95599200901215522', 余额=100000000}账户,存款金额为100000,当前金额为100000000
写:王五操作MyAccount{卡号='95599200901215522', 余额=100100000}账户成功,存款金额为100000,当前金额为100100000
读:王五他爹正在操作MyAccount{卡号='95599200901215522', 余额=100100000}账户,查询当前金额为100100000
写:王五他妹正在操作MyAccount{卡号='95599200901215522', 余额=100100000}账户,存款金额为100000,当前金额为100100000
写:王五他妹操作MyAccount{卡号='95599200901215522', 余额=100200000}账户成功,存款金额为100000,当前金额为100200000
写:王五他弟正在操作MyAccount{卡号='95599200901215522', 余额=100200000}账户,取款金额为-400000,当前金额为100200000
写:王五他弟操作MyAccount{卡号='95599200901215522', 余额=99800000}账户成功,取款金额为-400000,当前金额为99800000
写:王五正在操作MyAccount{卡号='95599200901215522', 余额=99800000}账户,存款金额为100000,当前金额为99800000
写:王五操作MyAccount{卡号='95599200901215522', 余额=99900000}账户成功,存款金额为100000,当前金额为99900000
读:王五正在操作MyAccount{卡号='95599200901215522', 余额=99900000}账户,查询当前金额为99900000