ReentrantLock简称可重入的互斥锁,当一个线程多次获取它所占有的锁资源时,是可以成功的,每当成功获取一次的时候,其保持计数将会+1,并且在其执行完毕释放锁的时候,保持计数随之清零;至于互斥的概念,就是当一个线程持有该锁时,其他需要获取该锁的线程将一直等待直至该锁被释放;这是多线程中实现同步的一种方式,它实现了synchronized的基本功能,同时也拓展了一些新的方法。
synchronized和ReentrantLock的区别:
除了synchronized的功能,多了三个高级功能:等待可中断,公平锁,绑定多个Condition.
1.等待可中断
在持有锁的线程长时间不释放锁的时候,等待的线程可以选择放弃等待. tryLock(long timeout, TimeUnit unit)
2.公平锁
按照申请锁的顺序来一次获得锁称为公平锁.synchronized的是非公平锁,ReentrantLock可以通过构造函数实现公平锁. new RenentrantLock(boolean fair)
3.绑定多个Condition
通过多次newCondition可以获得多个Condition对象,可以简单的实现比较复杂的线程同步的功能.通过await(),signal();
- 首先声明一个银行卡的辅助类
package com.test.demo;
public class BankCard {
private String cardNo;
private int balance;
public String getCardNo() {
return cardNo;
}
public void setCardNo(String cardNo) {
this.cardNo = cardNo;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
}
- 创建Husband类执行存钱操作
package com.test.demo;
import java.util.concurrent.locks.Lock;
public class Husband implements Runnable{
private BankCard card;
private Lock lock;
public Husband(BankCard card, Lock lock){
this.card = card;
this.lock = lock;
}
public void run() {
while(true){
lock.lock();
System.out.println("丈夫准备存钱,账户余额为: "+ card.getBalance());
card.setBalance(card.getBalance() + 500);
System.out.println("丈夫存钱完毕,账户余额为: " + card.getBalance());
lock.unlock();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 创建Wife类执行消费操作
package com.test.demo;
import java.util.concurrent.locks.Lock;
public class Wife implements Runnable{
private BankCard card;
private Lock lock;
public Wife(BankCard card, Lock lock){
this.card = card;
this.lock = lock;
}
public void run() {
while(true){
lock.lock();
System.out.println("妻子要消费,账户余额为: "+ card.getBalance());
card.setBalance(card.getBalance() - 1000);
System.out.println("妻子消费完毕,账户余额为:" +card.getBalance());
lock.unlock();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 启动线程进行测试
package com.test.demo;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
BankCard card = new BankCard();
card.setBalance(10000);
Lock lock = new ReentrantLock();
Wife child = new Wife(card, lock);
Husband father = new Husband(card, lock);
new Thread(child).start();
new Thread(father).start();
}
}
运行效果如下,我们可以看到正常操作
丈夫准备存钱,账户余额为: 10000
丈夫存钱完毕,账户余额为: 10500
妻子要消费,账户余额为: 10500
妻子消费完毕,账户余额为:9500
妻子要消费,账户余额为: 9500
妻子消费完毕,账户余额为:8500
丈夫准备存钱,账户余额为: 8500
丈夫存钱完毕,账户余额为: 9000
妻子要消费,账户余额为: 9000
妻子消费完毕,账户余额为:8000
丈夫准备存钱,账户余额为: 8000
丈夫存钱完毕,账户余额为: 8500
丈夫准备存钱,账户余额为: 8500
丈夫存钱完毕,账户余额为: 9000
妻子要消费,账户余额为: 9000
妻子消费完毕,账户余额为:8000
- 当妻子锁定后不释放锁
package com.test.demo;
import java.util.concurrent.locks.Lock;
public class Wife implements Runnable{
private BankCard card;
private Lock lock;
public Wife(BankCard card, Lock lock){
this.card = card;
this.lock = lock;
}
public void run() {
while(true){
lock.lock();
System.out.println("妻子要消费,账户余额为: "+ card.getBalance());
card.setBalance(card.getBalance() - 1000);
System.out.println("妻子消费完毕,账户余额为:" +card.getBalance());
//lock.unlock();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行效果如下:
妻子要消费,账户余额为: 10000
妻子消费完毕,账户余额为:9000
妻子要消费,账户余额为: 9000
妻子消费完毕,账户余额为:8000
妻子要消费,账户余额为: 8000
妻子消费完毕,账户余额为:7000
妻子要消费,账户余额为: 7000
妻子消费完毕,账户余额为:6000
妻子要消费,账户余额为: 6000
妻子消费完毕,账户余额为:5000
妻子要消费,账户余额为: 5000
妻子消费完毕,账户余额为:4000
妻子要消费,账户余额为: 4000
妻子消费完毕,账户余额为:3000
妻子要消费,账户余额为: 3000
妻子消费完毕,账户余额为:2000
妻子要消费,账户余额为: 2000
妻子消费完毕,账户余额为:1000
可以看到获取不到锁的Husband线程将一直处于阻塞状态!
除此之外,ReentrantLock提供了灵活的中断机制,第一种:ReentrantLock尝试获取锁时,可以指定等待的时间,当超过等待时间后则放弃执行并返回一个boolean值;第二种:ReentrantLock提供了可中断锁操作。
try {
if (lock.tryLock(5, TimeUnit.SECONDS)) { //如果已经被lock,尝试等待5s,看是否可以获得锁,如果5s后仍然无法获得锁则返回false继续执行
try {
//操作
} finally {
lock.unlock();
}
}
} catch (InterruptedException e) {
e.printStackTrace(); //当前线程被中断时(interrupt),会抛InterruptedException
}
以上为本次的简单演示,仅仅供个人的学习所用!