Java多线程的调度_动力节点Java学院整理
发布时间 - 2026-01-11 01:16:01 点击率:次有多个线程,如何控制它们执行的先后次序?

方法一:设置线程优先级。
java.lang.Thread 提供了 setPriority(int newPriority) 方法来设置线程的优先级,但线程的优先级是无法保障线程的执行次序的,优先级只是提高了优先级高的线程获取 CPU 资源的概率。也就是说,这个方法不靠谱。
方法二:使用线程合并。
使用 java.lang.Thread 的 join() 方法。比如有线程 a,现在当前线程想等待 a 执行完之后再往下执行,那就可以使用 a.join()。一旦线程使用了 a.join(),那么当前线程会一直等待 a 消亡之后才会继续执行。什么时候 a 消亡?a 的 run() 方法执行结束了 a 就消亡了。
这个方法可以有效地进行线程调度,但却只能局限于等待一个线程的执行调度。如果要等待 N 个线程的话,显然是无能为力了。而且等待线程必须在被等待线程消亡后才得到继续执行的指令,无法做到两个线程真正意义上的并发,灵活性较差。
方法三:使用线程通信。
java.lang.Object 提供了可以进行线程间通信的 wait 与 notify 、notifyAll 等方法。每个 Java 对象都有一个隐性的线程锁的概念,通过这个线程锁的概念我们让线程间可以进行通信,各线程不再埋头单干。著名的“生产者-消费者”模型就是基于这个原理实现的。
这个方法也可以有效地进行线程调度,而且也不仅仅局限于等待一个线程的执行调度,具有很大程度上的灵活性。但操作复杂,不易控制容易造成混乱,程序维护起来也不太方便。
方法四:使用闭锁。
闭锁就像一扇门,在先决条件未达成之前这扇门是闭着的,线程无法通过,先决条件达成之后,闭锁打开,线程就可以继续执行了。java.util.concurrent.CountDownLatch 是一个很实用的闭锁实现,它提供了 countDown() 和 await() 方法达成线程执行队列,这个方法最适合 M 个线程等待 N 个线程执行结束再执行的情况。首先初始化一个 CountDownLatch 对象,比如 CountDownLatch doneSignal = new CountDownLatch(N);该对象具有 N 作为计数阀值,每个被等待线程通过对 doneSignal 对象的持有,使用 countDown() 可以将 doneSignal 的计数阀值减一;每个等待线程通过对 doneSignal 对象的持有,使用 await() 阻塞当前线程,直到 doneSignal 计数阀值减为 0,才继续往下执行。
这个方法也可以有效地进行线程调度,而且比方法三更易于管理,开发者只需控制好 CountDownLatch 即可。但线程执行次序管理相对单一,它只是指出当前等待线程的数量,而且 CountDownLatch 的初始阀值一旦设置就只能递减下去,无法重置。如需递减过程中进行阀值的重置可以参考 java.util.concurrent.CyclicBarrier。
不管如何,CountDownLatch 对于一定条件下的线程队列的达成还是很有用的。对于复杂环境下的线程管理还是卓有成效的。所以熟悉和把握对它的使用还是很有必要的。
以下是一个实际项目中 CountDownLatch 的使用的例子:
private Map<Long,DecryptSignalAndPath> afterDecryptFilePathMap = new HashMap<Long,DecryptSignalAndPath>();//TODO 注意容器垃圾数据的清理工作
class DecryptRunnable implements Runnable {
private ServerFileBean serverFile;
private Long fid;//指向解密文件
private CountDownLatch decryptSignal;
protected DecryptRunnable(Long fid, ServerFileBean serverFile, CountDownLatch decryptSignal) {
this.fid = fid;
this.serverFile = serverFile;
this.decryptSignal = decryptSignal;
}
@Override
public void run() {
//开始解密
String afterDecryptFilePath = null;
DecryptSignalAndPath decryptSignalAndPath = new DecryptSignalAndPath();
decryptSignalAndPath.setDecryptSignal(decryptSignal);
afterDecryptFilePathMap.put(fid, decryptSignalAndPath);
afterDecryptFilePath = decryptFile(serverFile);
decryptSignalAndPath.setAfterDecryptFilePath(afterDecryptFilePath);
decryptSignal.countDown();//通知所有阻塞的线程
}
}
class DecryptSignalAndPath {
private String afterDecryptFilePath;
private CountDownLatch decryptSignal;
public String getAfterDecryptFilePath() {
return afterDecryptFilePath;
}
public void setAfterDecryptFilePath(String afterDecryptFilePath) {
this.afterDecryptFilePath = afterDecryptFilePath;
}
public CountDownLatch getDecryptSignal() {
return decryptSignal;
}
public void setDecryptSignal(CountDownLatch decryptSignal) {
this.decryptSignal = decryptSignal;
}
}
需要先执行的,被等待线程在这里加入:
CountDownLatch decryptSignal = new CountDownLatch(1);
new Thread(new DecryptRunnable(fid, serverFile, decryptSignal)).start();//无需拿到新线程句柄,由 CountDownLatch 自行跟踪
try {
decryptSignal.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
}
需要后执行,等待的线程可以这样加入:
CountDownLatch decryptSignal = afterDecryptFilePathMap.get(fid).getDecryptSignal();
try {
decryptSignal.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
}
当然,这也仅仅只是一个简单的 CountDownLatch 的使用展示,对于 CountDownLatch 来说有点大材小用了,因为它可以胜任更复杂的多线程环境。示例中的案例完全可以使用线程通信进行搞定。因为 CountDownLatch 的阀值初始为 1,所以这里甚至完全可以使用方法二所说的线程的合并进行取代。
如果读者觉得以上示例不够清晰,也可以参考 JDK API 提供的 demo,这个清晰明了:
class Driver2 { // ...
void main() throws InterruptedException {
CountDownLatch doneSignal = new CountDownLatch(N);
Executor e = ...
for (int i = 0; i < N; ++i) // create and start threads
e.execute(new WorkerRunnable(doneSignal, i));
doneSignal.await(); // wait for all to finish
}
}
class WorkerRunnable implements Runnable {
private final CountDownLatch doneSignal;
private final int i;
WorkerRunnable(CountDownLatch doneSignal, int i) {
this.doneSignal = doneSignal;
this.i = i;
}
public void run() {
try {
doWork(i);
doneSignal.countDown();
} catch (InterruptedException ex) {} // return;
}
void doWork() { ... }
}
以上所述是小编给大家介绍的Java多线程的调度,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!
# java
# 多线程调度
# 详解Java中的线程模型与线程调度
# Java线程调度之线程休眠用法分析
# Java 线程的优先级(setPriority)案例详解
# Java多线程与优先级详细解读
# java线程优先级原理详解
# Java线程的调度与优先级详解
# 有效地
# 是一个
# 也不
# 可以使用
# 小编
# 多线程
# 局限于
# 都有
# 大材小用
# 在这里
# 就像
# 那就
# 多个
# 在此
# 句柄
# 什么时候
# 才会
# 很有
# 只需
# 卓有成效
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
个人网站制作流程图片大全,个人网站如何注销?
Laravel怎么实现支付功能_Laravel集成支付宝微信支付
详解jQuery停止动画——stop()方法的使用
Laravel怎么实现软删除SoftDeletes_Laravel模型回收站功能与数据恢复【步骤】
php结合redis实现高并发下的抢购、秒杀功能的实例
Laravel如何生成和使用数据填充?(Seeder和Factory示例)
Laravel模型事件有哪些_Laravel Model Event生命周期详解
如何在橙子建站上传落地页?操作指南详解
网页设计与网站制作内容,怎样注册网站?
浏览器如何快速切换搜索引擎_在地址栏使用不同搜索引擎【搜索】
Win11应用商店下载慢怎么办 Win11更改DNS提速下载【修复】
使用spring连接及操作mongodb3.0实例
如何在 React 中条件性地遍历数组并渲染元素
家族网站制作贴纸教程视频,用豆子做粘帖画怎么制作?
香港服务器租用费用高吗?如何避免常见误区?
如何在香港服务器上快速搭建免备案网站?
JavaScript中如何操作剪贴板_ClipboardAPI怎么用
如何用虚拟主机快速搭建网站?详细步骤解析
Laravel怎么创建控制器Controller_Laravel路由绑定与控制器逻辑编写【指南】
Python文本处理实践_日志清洗解析【指导】
javascript中的try catch异常捕获机制用法分析
如何自己制作一个网站链接,如何制作一个企业网站,建设网站的基本步骤有哪些?
Laravel的HTTP客户端怎么用_Laravel HTTP Client发起API请求教程
iOS正则表达式验证手机号、邮箱、身份证号等
如何注册花生壳免费域名并搭建个人网站?
如何挑选高效建站主机与优质域名?
Laravel如何实现密码重置功能_Laravel密码找回与重置流程
Laravel如何实现多表关联模型定义_Laravel多对多关系及中间表数据存取【方法】
javascript中的数组方法有哪些_如何利用数组方法简化数据处理
Android利用动画实现背景逐渐变暗
phpredis提高消息队列的实时性方法(推荐)
Laravel如何优雅地处理服务层_在Laravel中使用Service层和Repository层
网站制作免费,什么网站能看正片电影?
个人摄影网站制作流程,摄影爱好者都去什么网站?
油猴 教程,油猴搜脚本为什么会网页无法显示?
Laravel怎么使用Session存储数据_Laravel会话管理与自定义驱动配置【详解】
如何使用 Go 正则表达式精准提取括号内首个纯字母标识符(忽略数字与嵌套)
Android使用GridView实现日历的简单功能
Laravel如何生成PDF或Excel文件_Laravel文档导出工具与使用教程
Laravel如何升级到最新的版本_Laravel版本升级流程与兼容性处理
音乐网站服务器如何优化API响应速度?
JavaScript中的标签模板是什么_它如何扩展字符串功能
在线制作视频的网站有哪些,电脑如何制作视频短片?
Laravel如何实现数据库事务?(DB Facade示例)
网站页面设计需要考虑到这些问题
如何在局域网内绑定自建网站域名?
如何用低价快速搭建高质量网站?
高防服务器租用如何选择配置与防御等级?
Android 常见的图片加载框架详细介绍
如何批量查询域名的建站时间记录?

