ReentrantLock介绍

ReentrantLock是一个可重入的互斥锁,又被称为“独占锁”。
顾名思义,ReentrantLock锁在同一个时间点只能被一个线程锁持有;而可重入的意思是,ReentrantLock锁,可以被单个线程多次获取。
ReentrantLock分为“公平锁”和“非公平锁”。它们的区别体现在获取锁的机制上是否公平。“锁”是为了保护竞争资源,防止多个线程同时操作线程而出错,ReentrantLock在同一个时间点只能被一个线程获取(当某线程获取到“锁”时,其它线程就必须等待);ReentraantLock是通过一个FIFO的等待队列来管理获取该锁所有线程的。在“公平锁”的机制下,线程依次排队获取锁;而“非公平锁”在锁是可获取状态时,不管自己是不是在队列的开头都会获取锁。
ReentrantLock函数列表
// 创建一个 ReentrantLock ,默认是“非公平锁”。 ReentrantLock() // 创建策略是fair的 ReentrantLock。fair为true表示是公平锁,fair为false表示是非公平锁。 ReentrantLock(boolean fair) // 查询当前线程保持此锁的次数。 int getHoldCount() // 返回目前拥有此锁的线程,如果此锁不被任何线程拥有,则返回 null。 protected Thread getOwner() // 返回一个 collection,它包含可能正等待获取此锁的线程。 protected Collection<Thread> getQueuedThreads() // 返回正等待获取此锁的线程估计数。 int getQueueLength() // 返回一个 collection,它包含可能正在等待与此锁相关给定条件的那些线程。 protected Collection<Thread> getWaitingThreads(Condition condition) // 返回等待与此锁相关的给定条件的线程估计数。 int getWaitQueueLength(Condition condition) // 查询给定线程是否正在等待获取此锁。 boolean hasQueuedThread(Thread thread) // 查询是否有些线程正在等待获取此锁。 boolean hasQueuedThreads() // 查询是否有些线程正在等待与此锁有关的给定条件。 boolean hasWaiters(Condition condition) // 如果是“公平锁”返回true,否则返回false。 boolean isFair() // 查询当前线程是否保持此锁。 boolean isHeldByCurrentThread() // 查询此锁是否由任意线程保持。 boolean isLocked() // 获取锁。 void lock() // 如果当前线程未被中断,则获取锁。 void lockInterruptibly() // 返回用来与此 Lock 实例一起使用的 Condition 实例。 Condition newCondition() // 仅在调用时锁未被另一个线程保持的情况下,才获取该锁。 boolean tryLock() // 如果锁在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁。 boolean tryLock(long timeout, TimeUnit unit) // 试图释放此锁。 void unlock()
ReentrantLock示例
通过对比“示例1”和“示例2”,我们能够清晰的认识lock和unlock的作用
示例1
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
// LockTest.java
// 仓库
class Depot {
private int size; // 仓库的实际数量
private Lock lock; // 独占锁
public Depot() {
this.size = 0;
this.lock = new ReentrantLock();
}
public void produce(int val) {
lock.lock();
try {
size += val;
System.out.printf("%s produce(%d) --> size=%d\n",
Thread.currentThread().getName(), val, size);
} finally {
lock.unlock();
}
}
public void consume(int val) {
lock.lock();
try {
size -= val;
System.out.printf("%s consume(%d) <-- size=%d\n",
Thread.currentThread().getName(), val, size);
} finally {
lock.unlock();
}
}
};
// 生产者
class Producer {
private Depot depot;
public Producer(Depot depot) {
this.depot = depot;
}
// 消费产品:新建一个线程向仓库中生产产品。
public void produce(final int val) {
new Thread() {
public void run() {
depot.produce(val);
}
}.start();
}
}
// 消费者
class Customer {
private Depot depot;
public Customer(Depot depot) {
this.depot = depot;
}
// 消费产品:新建一个线程从仓库中消费产品。
public void consume(final int val) {
new Thread() {
public void run() {
depot.consume(val);
}
}.start();
}
}
public class LockTest1 {
public static void main(String[] args) {
Depot mDepot = new Depot();
Producer mPro = new Producer(mDepot);
Customer mCus = new Customer(mDepot);
mPro.produce(60);
mPro.produce(120);
mCus.consume(90);
mCus.consume(150);
mPro.produce(110);
}
}
运行结果:
Thread-0 produce(60) --> size=60 Thread-1 produce(120) --> size=180 Thread-3 consume(150) <-- size=30 Thread-2 consume(90) <-- size=-60 Thread-4 produce(110) --> size=50
结果分析:
(01) Depot 是个仓库。通过produce()能往仓库中生产货物,通过consume()能消费仓库中的货物。通过独占锁lock实现对仓库的互斥访问:在操作(生产/消费)仓库中货品前,会先通过lock()锁住仓库,操作完之后再通过unlock()解锁。
(02) Producer是生产者类。调用Producer中的produce()函数可以新建一个线程往仓库中生产产品。
(03) Customer是消费者类。调用Customer中的consume()函数可以新建一个线程消费仓库中的产品。
(04) 在主线程main中,我们会新建1个生产者mPro,同时新建1个消费者mCus。它们分别向仓库中生产/消费产品。
根据main中的生产/消费数量,仓库最终剩余的产品应该是50。运行结果是符合我们预期的!
这个模型存在两个问题:
(01) 现实中,仓库的容量不可能为负数。但是,此模型中的仓库容量可以为负数,这与现实相矛盾!
(02) 现实中,仓库的容量是有限制的。但是,此模型中的容量确实没有限制的!
这两个问题,我们稍微会讲到如何解决。现在,先看个简单的示例2;通过对比“示例1”和“示例2”,我们能更清晰的认识lock(),unlock()的用途。
示例2
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
// LockTest.java
// 仓库
class Depot {
private int size; // 仓库的实际数量
private Lock lock; // 独占锁
public Depot() {
this.size = 0;
this.lock = new ReentrantLock();
}
public void produce(int val) {
// lock.lock();
// try {
size += val;
System.out.printf("%s produce(%d) --> size=%d\n",
Thread.currentThread().getName(), val, size);
// } catch (InterruptedException e) {
// } finally {
// lock.unlock();
// }
}
public void consume(int val) {
// lock.lock();
// try {
size -= val;
System.out.printf("%s consume(%d) <-- size=%d\n",
Thread.currentThread().getName(), val, size);
// } finally {
// lock.unlock();
// }
}
};
// 生产者
class Producer {
private Depot depot;
public Producer(Depot depot) {
this.depot = depot;
}
// 消费产品:新建一个线程向仓库中生产产品。
public void produce(final int val) {
new Thread() {
public void run() {
depot.produce(val);
}
}.start();
}
}
// 消费者
class Customer {
private Depot depot;
public Customer(Depot depot) {
this.depot = depot;
}
// 消费产品:新建一个线程从仓库中消费产品。
public void consume(final int val) {
new Thread() {
public void run() {
depot.consume(val);
}
}.start();
}
}
public class LockTest2 {
public static void main(String[] args) {
Depot mDepot = new Depot();
Producer mPro = new Producer(mDepot);
Customer mCus = new Customer(mDepot);
mPro.produce(60);
mPro.produce(120);
mCus.consume(90);
mCus.consume(150);
mPro.produce(110);
}
}
(某一次)运行结果:
Thread-0 produce(60) --> size=-60 Thread-4 produce(110) --> size=50 Thread-2 consume(90) <-- size=-60 Thread-1 produce(120) --> size=-60 Thread-3 consume(150) <-- size=-60
结果说明:
“示例2”在“示例1”的基础上去掉了lock锁。在“示例2”中,仓库中最终剩余的产品是-60,而不是我们期望的50。原因是我们没有实现对仓库的互斥访问。
示例3
在“示例3”中,我们通过Condition去解决“示例1”中的两个问题:“仓库的容量不可能为负数”以及“仓库的容量是有限制的”。
解决该问题是通过Condition。Condition是需要和Lock联合使用的:通过Condition中的await()方法,能让线程阻塞[类似于wait()];通过Condition的signal()方法,能让唤醒线程[类似于notify()]。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;
// LockTest.java
// 仓库
class Depot {
private int capacity; // 仓库的容量
private int size; // 仓库的实际数量
private Lock lock; // 独占锁
private Condition fullCondtion; // 生产条件
private Condition emptyCondtion; // 消费条件
public Depot(int capacity) {
this.capacity = capacity;
this.size = ;
this.lock = new ReentrantLock();
this.fullCondtion = lock.newCondition();
this.emptyCondtion = lock.newCondition();
}
public void produce(int val) {
lock.lock();
try {
// left 表示“想要生产的数量”(有可能生产量太多,需多此生产)
int left = val;
while (left > ) {
// 库存已满时,等待“消费者”消费产品。
while (size >= capacity)
fullCondtion.await();
// 获取“实际生产的数量”(即库存中新增的数量)
// 如果“库存”+“想要生产的数量”>“总的容量”,则“实际增量”=“总的容量”-“当前容量”。(此时填满仓库)
// 否则“实际增量”=“想要生产的数量”
int inc = (size+left)>capacity ? (capacity-size) : left;
size += inc;
left -= inc;
System.out.printf("%s produce(%d) --> left=%d, inc=%d, size=%d\n",
Thread.currentThread().getName(), val, left, inc, size);
// 通知“消费者”可以消费了。
emptyCondtion.signal();
}
} catch (InterruptedException e) {
} finally {
lock.unlock();
}
}
public void consume(int val) {
lock.lock();
try {
// left 表示“客户要消费数量”(有可能消费量太大,库存不够,需多此消费)
int left = val;
while (left > ) {
// 库存为时,等待“生产者”生产产品。
while (size <= )
emptyCondtion.await();
// 获取“实际消费的数量”(即库存中实际减少的数量)
// 如果“库存”<“客户要消费的数量”,则“实际消费量”=“库存”;
// 否则,“实际消费量”=“客户要消费的数量”。
int dec = (size<left) ? size : left;
size -= dec;
left -= dec;
System.out.printf("%s consume(%d) <-- left=%d, dec=%d, size=%d\n",
Thread.currentThread().getName(), val, left, dec, size);
fullCondtion.signal();
}
} catch (InterruptedException e) {
} finally {
lock.unlock();
}
}
public String toString() {
return "capacity:"+capacity+", actual size:"+size;
}
};
// 生产者
class Producer {
private Depot depot;
public Producer(Depot depot) {
this.depot = depot;
}
// 消费产品:新建一个线程向仓库中生产产品。
public void produce(final int val) {
new Thread() {
public void run() {
depot.produce(val);
}
}.start();
}
}
// 消费者
class Customer {
private Depot depot;
public Customer(Depot depot) {
this.depot = depot;
}
// 消费产品:新建一个线程从仓库中消费产品。
public void consume(final int val) {
new Thread() {
public void run() {
depot.consume(val);
}
}.start();
}
}
public class LockTest3 {
public static void main(String[] args) {
Depot mDepot = new Depot(100);
Producer mPro = new Producer(mDepot);
Customer mCus = new Customer(mDepot);
mPro.produce(60);
mPro.produce(120);
mCus.consume(90);
mCus.consume(150);
mPro.produce(110);
}
}
(某一次)运行结果:
Thread-0 produce( 60) --> left= 0, inc= 60, size= 60 Thread-1 produce(120) --> left= 80, inc= 40, size=100 Thread-2 consume( 90) <-- left= 0, dec= 90, size= 10 Thread-3 consume(150) <-- left=140, dec= 10, size= 0 Thread-4 produce(110) --> left= 10, inc=100, size=100 Thread-3 consume(150) <-- left= 40, dec=100, size= 0 Thread-4 produce(110) --> left= 0, inc= 10, size= 10 Thread-3 consume(150) <-- left= 30, dec= 10, size= 0 Thread-1 produce(120) --> left= 0, inc= 80, size= 80 Thread-3 consume(150) <-- left= 0, dec= 30, size= 50
以上所述是小编给大家介绍的Java concurrency之互斥锁,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!
# java
# concurrency
# 互斥锁
# 详解Java多线程编程中互斥锁ReentrantLock类的用法
# 详解java中的互斥锁信号量和多线程等待机制
# Java多线程并发编程(互斥锁Reentrant Lock)
# Java互斥锁简单实例
# Java多线程 ReentrantLock互斥锁详解
# 新建一个
# 与此
# 未被
# 互斥
# 是有
# 有可能
# 正在等待
# 能让
# 能为
# 类似于
# 小编
# 是一个
# 正等待
# 是个
# 太多
# 多个
# 在此
# 这两个
# 时间内
# 太大
相关文章:
南平网站制作公司,2025年南平市事业单位报名时间?
网站制作模板下载什么软件,ppt模板免费下载网站?
python的本地网站制作,如何创建本地站点?
如何通过PHP快速构建高效问答网站功能?
建站之星价格显示格式升级,你的预算足够吗?
高端智能建站公司优选:品牌定制与SEO优化一站式服务
如何自定义建站之星网站的导航菜单样式?
如何在IIS中新建站点并配置端口与IP地址?
seo网站制作优化,网站SEO优化步骤有哪些?
网站制作报价单模板图片,小松挖机官方网站报价?
php能控制zigbee模块吗_php通过串口与cc2530 zigbee通信【介绍】
文字头像制作网站推荐软件,醒图能自动配文字吗?
如何快速启动建站代理加盟业务?
已有域名如何快速搭建专属网站?
常州自助建站工具推荐:低成本搭建与模板选择技巧
建站之星免费模板:自助建站系统与智能响应式一键生成
教学论文网站制作软件有哪些,写论文用什么软件
?
湖州网站制作公司有哪些,浙江中蓝新能源公司官网?
网站制作哪家好,cc、.co、.cm哪个域名更适合做网站?
建站org新手必看:2024最新搭建流程与模板选择技巧
深圳网站制作的公司有哪些,dido官方网站?
简历在线制作网站免费版,如何创建个人简历?
建站之星上传入口如何快速找到?
如何在建站之星绑定自定义域名?
存储型VPS适合搭建中小型网站吗?
宝塔新建站点为何无法访问?如何排查?
如何选择香港主机高效搭建外贸独立站?
制作旅游网站html,怎样注册旅游网站?
如何在局域网内绑定自建网站域名?
网站建设设计制作营销公司南阳,如何策划设计和建设网站?
建站之星安装步骤有哪些常见问题?
如何通过多用户协作模板快速搭建高效企业网站?
济南网站制作的价格,历城一职专官方网站?
如何在IIS7上新建站点并设置安全权限?
如何快速搭建响应式可视化网站?
如何在景安云服务器上绑定域名并配置虚拟主机?
太原网站制作公司有哪些,网约车营运证查询官网?
如何用狗爹虚拟主机快速搭建网站?
如何解决VPS建站LNMP环境配置常见问题?
金*站制作公司有哪些,金华教育集团官网?
西安大型网站制作公司,西安招聘网站最好的是哪个?
关于BootStrap modal 在IOS9中不能弹出的解决方法(IOS 9 bootstrap modal ios 9 noticework)
详解ASP.NET 生成二维码实例(采用ThoughtWorks.QRCode和QrCode.Net两种方式)
一键制作网站软件下载安装,一键自动采集网页文档制作步骤?
如何高效配置香港服务器实现快速建站?
子杰智能建站系统|零代码开发与AI生成SEO优化指南
江苏网站制作公司有哪些,江苏书法考级官方网站?
建站之星如何优化SEO以实现高效排名?
如何配置IIS站点权限与局域网访问?
名字制作网站免费,所有小说网站的名字?
*请认真填写需求信息,我们会在24小时内与您取得联系。