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 }
以上为本次的简单演示,仅仅供个人的学习所用!