转自:http://www.verejava.com/?id=17236703718463
锁Lock取款机场景实例
Lock是一个接口,ReentrantLock是它的实现类,下面通过“取款机案例”来剖析它的4个常用方法。
1.爸爸妈妈同时在ATM上登录取款(不加任何锁)
public classBank {private static double money = 10000;public voidlogin(Thread currentUserThread) {
System.out.println(Thread.currentThread().getName()+ " 登录进入银行" + " 当前银行余额 : " +money);
}public voidlogout() {
System.out.println(Thread.currentThread().getName()+ " 退出银行");
}public double withdraw(doublewithdrawMoney) {if (this.money
System.out.println(Thread.currentThread().getName()+ " 当前银行余额 : " + this.money + " 余额不够");return 0;
}this.money -=withdrawMoney;
System.out.println(Thread.currentThread().getName()+ " 取款 : " + withdrawMoney + " 当前银行余额 : " + this.money);returnwithdrawMoney;
}
}
public classTestLock {public static voidmain(String[] args) {final Bank bank = newBank();//启动爸爸线程
Thread fatherThread = new Thread("爸爸") {public voidrun() {try{//爸爸登录
bank.login(Thread.currentThread());//过2秒取10000
Thread.sleep(2000);
bank.withdraw(10000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
};
fatherThread.start();//启动妈妈线程
Thread motherThread = new Thread("妈妈") {public voidrun() {try{//妈妈登录
bank.login(Thread.currentThread());//过5秒取 10000
Thread.sleep(5000);
bank.withdraw(10000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
};
motherThread.start();
}
}
结果:
妈妈 登录进入银行 当前银行余额 : 10000.0爸爸 登录进入银行 当前银行余额 :10000.0爸爸 取款 :10000.0 当前银行余额 : 0.0妈妈 当前银行余额 :0.0 余额不够
妈妈登录显示银行余额为10000,但是当她取钱时却显示“余额不足”,产生了数据不一致。
2.同一时刻,爸爸或妈妈线程只能有一个能够登录银行取款(获取lock()),另外一个线程需要等待, 直到unlock()释放锁
public classBank {private static double money = 10000;private Lock lock = newReentrantLock();public voidlogin(Thread currentUserThread) {
lock.lock();//登录加锁
System.out.println(Thread.currentThread().getName() + " 登录进入银行" + " 当前银行余额 : " +money);
}public voidlogout() {
lock.unlock();//退出释放锁
System.out.println(Thread.currentThread().getName() + " 退出银行");
}public double withdraw(doublewithdrawMoney) {if (this.money
System.out.println(Thread.currentThread().getName()+ " 当前银行余额 : " + this.money + " 余额不够");return 0;
}this.money -=withdrawMoney;
System.out.println(Thread.currentThread().getName()+ " 取款 : " + withdrawMoney + " 当前银行余额 : " + this.money);returnwithdrawMoney;
}
}
public classTestLock {public static voidmain(String[] args) {final Bank bank = newBank();//启动爸爸线程
Thread fatherThread = new Thread("爸爸") {public voidrun() {try{//爸爸登录
bank.login(Thread.currentThread());//过2秒取10000
Thread.sleep(2000);
bank.withdraw(10000);//爸爸退出
bank.logout();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
};
fatherThread.start();//启动妈妈线程
Thread motherThread = new Thread("妈妈") {public voidrun() {try{//妈妈登录
bank.login(Thread.currentThread());//过5秒取 10000
Thread.sleep(5000);
bank.withdraw(10000);//妈妈退出
bank.logout();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
};
motherThread.start();
}
}
结果:
爸爸 登录进入银行 当前银行余额 : 10000.0爸爸 取款 :10000.0 当前银行余额 : 0.0爸爸 退出银行
妈妈 登录进入银行 当前银行余额 :0.0妈妈 当前银行余额 :0.0余额不够
妈妈 退出银行
lock() 和unlock()成对出现,在login(Thread currentUserThread) 登录方法中调用 lock() ,在 logout()退出方法中调用了unlock()。
也就是说,Lock类的锁机制允许在不同的方法中加锁和解锁,而synchronized关键字只能在同一个方法中加锁和解锁。
3. 通过tryLock()判断是否可以获得锁, 能获得锁返回true,否则返回false
public classBank {private static double money = 10000;private Lock lock = newReentrantLock();public voidlogin(Thread currentUserThread) {//判断是否已经有线程登录
if (!lock.tryLock()) {
System.out.println(Thread.currentThread().getName()+ " 有人已经登录进入银行 请稍等");
}else{
System.out.println(Thread.currentThread().getName()+ " 登录进入银行" + " 当前银行余额: " +money);
}
}public voidlogout() {
lock.unlock();//退出释放锁
System.out.println(Thread.currentThread().getName() + " 退出银行");
}public double withdraw(doublewithdrawMoney) {if (this.money
System.out.println(Thread.currentThread().getName()+ " 当前银行余额: " + this.money + " 余额不够");return 0;
}this.money -=withdrawMoney;
System.out.println(Thread.currentThread().getName()+ " 取款: " + withdrawMoney + " 当前银行余额: " + this.money);returnwithdrawMoney;
}
}
public classTestLock {public static voidmain(String[] args) {final Bank bank = newBank();//启动爸爸线程
Thread fatherThread = new Thread("爸爸") {public voidrun() {try{//爸爸登录
bank.login(Thread.currentThread());//过2秒取10000
Thread.sleep(2000);
bank.withdraw(10000);//爸爸退出
bank.logout();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
};
fatherThread.start();//启动妈妈线程
Thread motherThread = new Thread("妈妈") {public voidrun() {//妈妈登录
bank.login(Thread.currentThread());
}
};
motherThread.start();
}
}
结果:
妈妈 有人已经登录进入银行 请稍等
爸爸 登录进入银行 当前银行余额:10000.0爸爸 取款:10000.0 当前银行余额: 0.0爸爸 退出银行
4. 通过tryLock(long time,TimeUnit timeUnit)设置一段时间后重新进入
public classBank {private static double money = 10000;private Lock lock = newReentrantLock();public voidlogin(Thread currentUserThread) {//如果登录不成功,10秒后再重新尝试获得锁
try{if (!lock.tryLock(10, TimeUnit.SECONDS)) {
System.out.println(Thread.currentThread().getName()+ " 有人已经登录进入银行,请稍等");
}else{
System.out.println(Thread.currentThread().getName()+ " 登录进入银行" + " 当前银行余额: " +money);
}
}catch(InterruptedException e) {
e.printStackTrace();
}
}public voidlogout() {
lock.unlock();//退出释放锁
System.out.println(Thread.currentThread().getName() + " 退出银行");
}public double withdraw(doublewithdrawMoney) {if (this.money
System.out.println(Thread.currentThread().getName()+ " 当前银行余额: " + this.money + " 余额不够");return 0;
}this.money -=withdrawMoney;
System.out.println(Thread.currentThread().getName()+ " 取款: " + withdrawMoney + " 当前银行余额: " + this.money);returnwithdrawMoney;
}
}
public classTestLock {public static voidmain(String[] args) {final Bank bank = newBank();//启动爸爸线程
Thread fatherThread = new Thread("爸爸") {public voidrun() {try{//爸爸登录
bank.login(Thread.currentThread());//过2秒取10000
Thread.sleep(2000);
bank.withdraw(10000);//爸爸退出
bank.logout();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
};
fatherThread.start();//启动妈妈线程
Thread motherThread = new Thread("妈妈") {public voidrun() {//妈妈登录
bank.login(Thread.currentThread());
}
};
motherThread.start();
}
}
结果妈妈10秒后重新尝试得到锁登录进入了银行 :
爸爸 登录进入银行 当前银行余额: 10000.0爸爸 取款:10000.0 当前银行余额: 0.0爸爸 退出银行
妈妈 登录进入银行 当前银行余额:0.0
5.如果爸爸线程调用 lock() 获得锁以后,没有unlock()释放锁,妈妈线程将会一直等待产生死锁,即使调用 interrupt()中断也没有作用。
public classBank {private static double money = 10000;private Lock lock = newReentrantLock();public voidlogin(Thread currentUserThread) {
lock.lock();
System.out.println(Thread.currentThread().getName()+ " 登录进入银行" + " 当前银行余额 : " +money);
}public voidlogout() {
lock.unlock();//退出释放锁
System.out.println(Thread.currentThread().getName() + " 退出银行");
}public double withdraw(doublewithdrawMoney) {if (this.money
System.out.println(Thread.currentThread().getName()+ " 当前银行余额: " + this.money + " 余额不够");return 0;
}this.money -=withdrawMoney;
System.out.println(Thread.currentThread().getName()+ " 取款: " + withdrawMoney + " 当前银行余额: " + this.money);returnwithdrawMoney;
}
}
public classTestLock {public static voidmain(String[] args) {final Bank bank = newBank();//启动爸爸线程
Thread fatherThread = new Thread("爸爸") {public voidrun() {try{//爸爸登录
bank.login(Thread.currentThread());//过2秒取10000
Thread.sleep(2000);
bank.withdraw(10000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
};
fatherThread.start();//启动妈妈线程
Thread motherThread = new Thread("妈妈") {public voidrun() {try{
Thread.sleep(1000);
bank.login(Thread.currentThread());
}catch(InterruptedException e) {
System.out.println(Thread.currentThread().getName()+" 登录超时被中断");
}
}
};
motherThread.start();try{
Thread.sleep(2000);
}catch(InterruptedException e) {
e.printStackTrace();
}
motherThread.interrupt();
}
}
结果:
妈妈线程一直等待爸爸线程释放锁,结果造成死锁。妈妈线程调用motherThread.interrupt(); 中断也不起做用。
6. 在登录获得锁时,调用lockInterruptibly(),允许中断等待的线程,使motherThread.interrupt()可用。
public classBank {private static double money = 10000;private Lock lock = newReentrantLock();public void login(Thread currentUserThread) throwsInterruptedException {
lock.lockInterruptibly();//运行等待线程被中断
System.out.println(Thread.currentThread().getName() + " 登录进入银行" + " 当前银行余额 : " +money);
}public voidlogout() {
lock.unlock();//退出释放锁
System.out.println(Thread.currentThread().getName() + " 退出银行");
}public double withdraw(doublewithdrawMoney) {if (this.money
System.out.println(Thread.currentThread().getName()+ " 当前银行余额: " + this.money + " 余额不够");return 0;
}this.money -=withdrawMoney;
System.out.println(Thread.currentThread().getName()+ " 取款: " + withdrawMoney + " 当前银行余额: " + this.money);returnwithdrawMoney;
}
}
public classTestLock {public static voidmain(String[] args) {final Bank bank = newBank();//启动爸爸线程
Thread fatherThread = new Thread("爸爸") {public voidrun() {try{//爸爸登录
bank.login(Thread.currentThread());//过2秒取10000
Thread.sleep(2000);
bank.withdraw(10000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
};
fatherThread.start();//启动 妈妈 线程
Thread motherThread = new Thread("妈妈") {public voidrun() {try{
Thread.sleep(1000);
bank.login(Thread.currentThread());
}catch(InterruptedException e) {
System.out.println(Thread.currentThread().getName()+" 登录超时被中断");
}
}
};
motherThread.start();try{
Thread.sleep(2000);
}catch(InterruptedException e) {
e.printStackTrace();
}
motherThread.interrupt();
}
}
结果:
爸爸 登录进入银行 当前银行余额 : 10000.0妈妈 登录超时被中断
爸爸 取款:10000.0 当前银行余额: 0.0