多线程---ReentrantLock
package com.test;
import java.util.Collection;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; // LockTest1.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);
//4个线程,2个线程操作produce方法,2个线程操作consume方法。4个线程使用同一个锁,每次只能由一个线程执行。
mPro.produce(60);
mPro.produce(120);
mCus.consume(90);
mCus.consume(150);
}
}
ReentrantLock是一个可重入的互斥锁,又被称为“独占锁”。 顾名思义,ReentrantLock锁在同一个时间点只能被一个线程锁持有;而可重入的意思是,ReentrantLock锁,可以被单个线程多次获取。
ReentrantLock分为“公平锁”和“非公平锁”。它们的区别体现在获取锁的机制上是否公平。“锁”是为了保护竞争资源,防止多个线程同时操作线程而出错,ReentrantLock在同一个时间点只能被一个线程获取(当某线程获取到“锁”时,其它线程就必须等待);ReentraantLock是通过一个FIFO的等待队列来管理获取该锁所有线程的。在“公平锁”的机制下,线程依次排队获取锁;而“非公平锁”在锁是可获取状态时,不管自己是不是在队列的开头都会获取锁。
Condition的作用是对锁进行更精确的控制。Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。不同的是,Object中的wait(),notify(),notifyAll()方法是和"同步锁"(synchronized关键字)捆绑使用的;而Condition是需要与"互斥锁"/"共享锁"捆绑使用的。
Condition函数列表 复制代码
// 造成当前线程在接到信号或被中断之前一直处于等待状态。
void await()
// 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
boolean await(long time, TimeUnit unit)
// 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
long awaitNanos(long nanosTimeout)
// 造成当前线程在接到信号之前一直处于等待状态。
void awaitUninterruptibly()
// 造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。
boolean awaitUntil(Date deadline)
// 唤醒一个等待线程。
void signal()
// 唤醒所有等待线程。
void signalAll()
Condition示例 示例1是通过Object的wait(), notify()来演示线程的休眠/唤醒功能。
示例2是通过Condition的await(), signal()来演示线程的休眠/唤醒功能。
示例3是通过Condition的高级功能。 示例1 复制代码
public class WaitTest1 { public static void main(String[] args) { ThreadA ta = new ThreadA("ta"); synchronized(ta) { // 通过synchronized(ta)获取“对象ta的同步锁”
try {
System.out.println(Thread.currentThread().getName()+" start ta");
ta.start(); System.out.println(Thread.currentThread().getName()+" block");
ta.wait(); // 等待 System.out.println(Thread.currentThread().getName()+" continue");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} static class ThreadA extends Thread{ public ThreadA(String name) {
super(name);
} public void run() {
synchronized (this) { // 通过synchronized(this)获取“当前对象的同步锁”
System.out.println(Thread.currentThread().getName()+" wakup others");
notify(); // 唤醒“当前对象上的等待线程”
}
}
}
}
复制代码 示例2 复制代码
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock; public class ConditionTest1 { private static Lock lock = new ReentrantLock();
private static Condition condition = lock.newCondition(); public static void main(String[] args) { ThreadA ta = new ThreadA("ta"); lock.lock(); // 获取锁
try {
System.out.println(Thread.currentThread().getName()+" start ta");
ta.start(); System.out.println(Thread.currentThread().getName()+" block");
condition.await(); // 等待,会释放锁, System.out.println(Thread.currentThread().getName()+" continue");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 释放锁
}
} static class ThreadA extends Thread{ public ThreadA(String name) {
super(name);
} public void run() {
lock.lock(); // 获取锁
try {
System.out.println(Thread.currentThread().getName()+" wakup others");
condition.signal(); // 唤醒“condition所在锁上的其它线程”
} finally {
lock.unlock(); // 释放锁
}
}
}
}
复制代码
运行结果: main start ta
main block
ta wakup others
main continue
通过“示例1”和“示例2”,我们知道Condition和Object的方法有一下对应关系: Object Condition
休眠 wait await
唤醒个线程 notify signal
唤醒所有线程 notifyAll signalAll
Condition除了支持上面的功能之外,它更强大的地方在于:能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition。
例如,假如多线程读/写同一个缓冲区:当向缓冲区中写入数据之后,唤醒"读线程";当从缓冲区读出数据之后,唤醒"写线程";并且当缓冲区满的时候,"写线程"需要等待;当缓冲区为空时,"读线程"需要等待。 如果采用Object类中的wait(), notify(), notifyAll()实现该缓冲区,当向缓冲区写入数据之后需要唤醒"读线程"时,不可能通过notify()或notifyAll()明确的指定唤醒"读线程",而只能通过notifyAll唤醒所有线程(但是notifyAll无法区分唤醒的线程是读线程,还是写线程)。 但是,通过Condition,就能明确的指定唤醒读线程。
看看下面的示例3,可能对这个概念有更深刻的理解。
满的时候只能减,空的时候只能加。加和减是2个线程组。
package com.test;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition; // LockTest3.java
// 仓库
class Depot1 {
private int capacity; // 仓库的容量
private int size; // 仓库的实际数量
private Lock lock; // 独占锁
private Condition fullCondtion; // 生产条件
private Condition emptyCondtion; // 消费条件 public Depot1(int capacity) {
this.capacity = capacity;
this.size = 0;
this.lock = new ReentrantLock();
this.fullCondtion = lock.newCondition();
this.emptyCondtion = lock.newCondition();
} public void produce(int val) {
lock.lock();
try {
// left 表示“想要生产的数量”(有可能生产量太多,需多此生产)
while (val > 0) {
// 库存已满时,等待“消费者”消费产品。
while (size >= capacity)
fullCondtion.await(); //等待,释放锁
// 获取“实际生产的数量”(即库存中新增的数量)
// 如果“库存”+“想要生产的数量”>“总的容量”,则“实际增量”=“总的容量”-“当前容量”。(此时填满仓库)
// 否则“实际增量”=“想要生产的数量”
size += val;
System.out.printf("%s produce --> val=%3d, size=%3d\n",
Thread.currentThread().getName(), val, size);
// 通知“消费者”可以消费了。
emptyCondtion.signal();
}
} catch (InterruptedException e) {
} finally {
lock.unlock();
}
} public void consume(int val) {
lock.lock();
try {
// left 表示“客户要消费数量”(有可能消费量太大,库存不够,需多此消费)
while (val > 0) {
// 库存为0时,等待“生产者”生产产品。
while (size <= 0)
emptyCondtion.await();
// 获取“实际消费的数量”(即库存中实际减少的数量)
// 如果“库存”<“客户要消费的数量”,则“实际消费量”=“库存”;
// 否则,“实际消费量”=“客户要消费的数量”。
size -= val;
System.out.printf("%s consume<-- val=%3d, size=%3d\n",
Thread.currentThread().getName(), val, size);
fullCondtion.signal();
}
} catch (InterruptedException e) {
} finally {
lock.unlock();
}
} public String toString() {
return "capacity:"+capacity+", actual size:"+size;
}
}; // 生产者
class Producer1 {
private Depot1 depot; public Producer1(Depot1 depot) {
this.depot = depot;
} // 消费产品:新建一个线程向仓库中生产产品。
public void produce(final int val) {
new Thread() {
public void run() {
depot.produce(val);
}
}.start();
}
} // 消费者
class Customer1 {
private Depot1 depot; public Customer1(Depot1 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) {
Depot1 mDepot = new Depot1(100);
Producer1 mPro = new Producer1(mDepot);
Customer1 mCus = new Customer1(mDepot); //5个线程共用同一把锁。2个线程减3个线程加。
mPro.produce(60);
mPro.produce(120);
mCus.consume(90);
mCus.consume(150);
mPro.produce(110);
}
}
多线程---ReentrantLock的更多相关文章
- 深入浅出多线程——ReentrantLock (二)
深入浅出多线程——ReentrantLock (一)文章中介绍了该类的基本使用,以及在源码的角度分析lock().unlock()方法.这次打算在此基础上介绍另一个极为重要的方法newConditio ...
- Java多线程——ReentrantLock源码阅读
上一章<AQS源码阅读>讲了AQS框架,这次讲讲它的应用类(注意不是子类实现,待会细讲). ReentrantLock,顾名思义重入锁,但什么是重入,这个锁到底是怎样的,我们来看看类的注解 ...
- java多线程 ReentrantLock
本章对ReentrantLock包进行基本介绍,这一章主要对ReentrantLock进行概括性的介绍,内容包括:ReentrantLock介绍ReentrantLock函数列表ReentrantLo ...
- 深入浅出多线程——ReentrantLock (一)
ReentrantLock是一个排它重入锁,与synchronized关键字语意类似,但比其功能更为强大.该类位于java.util.concurrent.locks包下,是Lock接口的实现类.基本 ...
- java多线程---ReentrantLock源码分析
ReentrantLock源码分析 基础知识复习 synchronized和lock的区别 synchronized是非公平锁,无法保证线程按照申请锁的顺序获得锁,而Lock锁提供了可选参数,可以配置 ...
- java多线程(三)线程的安全问题
1.1. 什么是线程安全 如果有多个线程同时运行同一个实现了Runnable接口的类,程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的:反之,则是线程不 ...
- Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock
本章对ReentrantLock包进行基本介绍,这一章主要对ReentrantLock进行概括性的介绍,内容包括:ReentrantLock介绍ReentrantLock函数列表ReentrantLo ...
- 高级java必会系列二:多线程经常使用的3个关键字:synchronized、ReentrantLock、volatile
系列一讲解了多线程,本章讲解多线程开发中经常使用到的3个关键字synchronized.ReentrantLock.volatile. 一.synchronized 互斥锁,即操作互斥,并发线程过来, ...
- Java多线程的~~~Lock接口和ReentrantLock使用
在多线程开发.除了synchronized这个keyword外,我们还通过Lock接口来实现这样的效果.由Lock接口来实现 这样的多线程加锁效果的优点是非常的灵活,我们不在须要对整个函数加锁,并且能 ...
随机推荐
- springMVC访问 WEB-INF 下的 jsp 和 html
配置freemarker,记得加上jar包 <?xml version="1.0" encoding="UTF-8"?> <beans xml ...
- nginx动静分离小示例
server { listen ; server_name www.xxx.cn; #静态页面 #匹配首页,url:www.xxx.cn index index.html; root /usr/loc ...
- yii---模型的创建
在 model/ 路径新建 Test.php 模型 我们类的名称一定要与数据表的名称相同. 继承 yii\db\ActiveRecord 类: 在模型类中 声明 tableName() 指定表名 // ...
- 7.19python昨日复习和多线程(2)
2018-7-19 21:39:49 我觉得这次的笔记是非常非常完美的!!!明天继续 睡觉去啦! 傍黑时候和晴宝打电话,她特能说,很喜欢这种感觉,有好多东西要和你分享! 1.复习! # !/usr/b ...
- hadoop命令运行,去除:WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform 警告
参照:Hadoop之—— WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... 修 ...
- canvas API笔记
HTML5添加的最受欢迎的功能就是<canvas>元素,这个元素负责在页面的指定区域通过js动态地在这个区域绘制图形. <canvas> 由几组API构成. 准备: <c ...
- oracle 日期格式化和数据去重
1.获取系统日期: select sysdate as date1 from dual: 当前时间减去7分钟的时间 select sysdate,sysdate - interval '7' MINU ...
- webView内部跳转后 返回不行了
if (navigationType == UIWebViewNavigationTypeBackForward) { return YES; } 重定向?
- 访问GitLab的PostgreSQL数据库-(3)
1.登陆gitlab的安装服务查看配置文件 cat /var/opt/gitlab/gitlab-rails/etc/database.yml production: adapter: postgre ...
- (3.5)mysql基础深入——mysqld_safe脚本功能及流程
(3.5)mysql基础深入——mysqld_safe脚本功能及流程 目录 1.mysqld_safe过程总结 2.mysql_safe启动的好处 3.mysqld_safe 参数 4.mysqld_ ...