【java进阶之路】(并发编程篇)1.Java线程
Java线程
创建和运行线程的方式
直接使用 Thread
// 创建线程对象Thread t = new Thread() {public void run() {// 要执行的任务}};// 启动线程t.start();
使用 Runnable 配合 Thread
把【线程】和【任务】(要执行的代码)分开
- Thread 代表线程
- Runnable 可运行的任务(线程要执行的代码)
Runnable runnable = new Runnable() {public void run() {// 要执行的任务}};// 创建线程对象Thread t = new Thread(runnable);// 启动线程t.start();
FutureTask 结合 Callable 配合 Thread
FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况
// 创建任务对象
FutureTask<Integer> task3 = new FutureTask<>(() -> {log.debug("hello");return 100;
});
// 参数1 是任务对象; 参数2 是线程名字,推荐
new Thread(task3, "t3").start();
// 主线程阻塞,同步等待 task 执行完毕的结果
Integer result = task3.get();
log.debug("结果是:{}", result);
线程运行的原理
栈与栈帧
每个线程启动后,虚拟机就会为其分配一块栈内存。
- 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
- 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法
线程上下文切换
因为一些原因导致 cpu 不再执行当前线程 , 转而执行另一个线程的代码
- 线程的 cpu 时间片用完
- 垃圾回收
- 有更优先的线程需要运行
- 线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法
常见API
start 与 run
- 直接调用 run 是在主线程中执行了 run,没有启动新的线程
- 使用 start 是启动新的线程,通过新的线程间接执行 run 中的代码
sleep 与 yield
sleep
调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)
其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException
睡眠结束后的线程未必会立刻得到执行
建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性
yield
调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程
具体的实现依赖于操作系统的任务调度器(如果当前只有一个线程执行 , 那么在让出后 , 调度器仍会继续选择这个线程继续分配时间片)
join 与 join(long n)
join是一个让正在运行的线程等待某个其他线程直到其执行结束的方法 , 可以为它指定参数来规定最多等待多少秒
interrupt
- 打断 sleep 的线程 , 会清空打断状态(将打断标记设为false)
- 打断正常运行的线程 , 不会清空打断状态
模式之两阶段终止
在一个线程 T1 中如何“优雅”终止线程 T2?这里的【优雅】指的是给 T2 一个料理后事的机会。
class TPTInterrupt {private Thread thread;public void start() {thread = new Thread(() -> {while (true) {Thread current = Thread.currentThread();if (current.isInterrupted()) {log.debug("料理后事");break;}try {Thread.sleep(1000);//情况一 : log.debug("将结果保存");//情况二} catch (InterruptedException e) {current.interrupt();}}// 执行监控操作},"监控线程");thread.start();
}public void stop() {thread.interrupt();}
打断park线程
private static void test3() throws InterruptedException {Thread t1 = new Thread(() -> {log.debug("park...");LockSupport.park();log.debug("unpark...");log.debug("打断状态:{}", Thread.currentThread().isInterrupted());}, "t1");t1.start();sleep(0.5);t1.interrupt();}
不推荐的方法
容易破坏同步代码块 , 造成线程死锁
- stop()
- suspend()
- resume()
主线程与守护线程
默认情况下,Java 进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束。
log.debug("开始运行...");Thread t1 = new Thread(() -> {log.debug("开始运行...");sleep(2);log.debug("运行结束...");}, "daemon");// 设置该线程为守护线程t1.setDaemon(true);t1.start();sleep(1);log.debug("运行结束...");
应用场景
- 垃圾回收器线程就是一种守护线程
- Tomcat 中的 Acceptor 和 Poller 线程都是守护线程,所以 Tomcat 接收到 shutdown 命令后,不会等待它们处理完当前请求
五种状态与六种状态
五种状态
从操作系统层面来说
- 【初始状态】仅是在语言层面创建了线程对象,还未与操作系统线程关联
- 【可运行状态】(就绪状态)指该线程已经被创建(与操作系统线程关联),可以由 CPU 调度执行
- 【运行状态】指获取了 CPU 时间片运行中的状态
- 当 CPU 时间片用完,会从【运行状态】转换至【可运行状态】,会导致线程的上下文切换
- 【阻塞状态】
- 如果调用了阻塞 API,如 BIO 读写文件,这时该线程实际不会用到 CPU,会导致线程上下文切换,进入【阻塞状态】
- 等 BIO 操作完毕,会由操作系统唤醒阻塞的线程,转换至【可运行状态】
- 与【可运行状态】的区别是,对【阻塞状态】的线程来说只要它们一直不唤醒,调度器就一直不会考虑调度它们
- 【终止状态】表示线程已经执行完毕,生命周期已经结束,不会再转换为其它状态
六种状态
从Java API层面来说
- NEW 线程刚被创建,但是还没有调用 start() 方法
- RUNNABLE 当调用了 start() 方法之后,注意,Java API 层面的 RUNNABLE 状态涵盖了 操作系统 层面的【可运行状态】、【运行状态】和【阻塞状态】(由于 BIO 导致的线程阻塞,在 Java 里无法区分,仍然认为是可运行)
- BLOCKED , WAITING , TIMED_WAITING 都是 Java API 层面对【阻塞状态】的细分
- TERMINATED 当线程代码运行结束
当调用了 start() 方法之后,注意,Java API 层面的 RUNNABLE 状态涵盖了 操作系统 层面的【可运行状态】、【运行状态】和【阻塞状态】(由于 BIO 导致的线程阻塞,在 Java 里无法区分,仍然认为是可运行) - BLOCKED , WAITING , TIMED_WAITING 都是 Java API 层面对【阻塞状态】的细分
- TERMINATED 当线程代码运行结束
【java进阶之路】(并发编程篇)1.Java线程相关推荐
- Scala进阶之路-并发编程模型Akka入门篇
Scala进阶之路-并发编程模型Akka入门篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Akka Actor介绍 1>.Akka介绍 写并发程序很难.程序员不得不处 ...
- JAVA学习笔记(并发编程-叁)- 线程安全性
文章目录 线程安全性-原子性 原子性-Atomic包 AtomicXXX: CAS, Unsafe.compareAndSwapInt AtomicLong LongAdder AtomicRefer ...
- 《java 进阶之路》 上--推荐书籍
整整一月没有更新博客了,因为十月份和双十一新入手的6本技术相关的书,这个月看完了3本,后面的3本还得慢慢肯. 下面我就介绍下进阶高级工程师必须也是我自己都看的一些书和知识点. 1.深入理解Java虚拟 ...
- 《Java 进阶之路》 下--推荐书籍
真正想提升自己,我感觉最主要的是先把 JVM.并发.网络这三块知识点学会.学通,这三块是基础,后面所有的框架.中间件等相关的都是基于这三块知识点之上的.学完这三块知识点,可以快速的掌握其它的知识,新框 ...
- Scala进阶之路-面向对象编程之类的成员详解
Scala进阶之路-面向对象编程之类的成员详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Scala中的object对象及apply方法 1>.scala 单例对象 ...
- [Java并发编程(二)] 线程池 FixedThreadPool、CachedThreadPool、ForkJoinPool?为后台任务选择合适的 Java executors...
[Java并发编程(二)] 线程池 FixedThreadPool.CachedThreadPool.ForkJoinPool?为后台任务选择合适的 Java executors ... 摘要 Jav ...
- java 线程钩子_高级并发编程系列六(线程池钩子函数)
1.考考你 国庆假期快要结束了,准备回到工作岗位的你,是不是已经开始撸起袖子敲代码,反正发完文章我就要准备去加班了,程序员就这样,有干劲对吧 那么来吧,让我们一起分享完高级并发编程系列中,线程池小节的 ...
- 并发编程5:Java 阻塞队列源码分析(下)
上一篇 并发编程4:Java 阻塞队列源码分析(上) 我们了解了 ArrayBlockingQueue, LinkedBlockingQueue 和 PriorityBlockingQueue,这篇文 ...
- 个人开发经历--我的java学习之路(学校篇)
个人开发经历--我的java学习之路(学校篇) 个人介绍: 姓名: 不在这里说明 联系信息: 个人历程 jdbc阶段 sql生成器 一代代码生成器 servlet阶段 servlet项目中,sql生成 ...
- 多线程进阶=》JUC并发编程
多线程进阶=>JUC并发编程 1.什么是JUC JUC是java.util.concurrent的简写. 用中文概括一下,JUC的意思就是java并发编程工具包. 并发编程的本质就是 ...
最新文章
- 2022-2028年中国氢化环氧树脂产业发展动态及投资趋势预测报告
- 美国国防部作战指挥系统桌面虚拟化案例视频(中文配音版)
- 概率编程库Pymc3案例之鲁棒线性回归
- HBase 数据导入功能实现方式解释
- 【Linux系统编程】线程同步与互斥:读写锁
- 面向数智营销的 AI FAAS 解决方案
- MYSQL 碎片查询
- 信息系统基础知识---企业信息化与电子商务
- 仿映客直播礼物特效制作流程
- 第一个RSF自动化测试用例-启动打开百度页面,搜索【月饼】关键字,关闭浏览器...
- Android面试分析一:关于OKhttp详解(附带视频教程,flutter游戏全屏
- MATLAB回归分析命令——regress命令
- 十六进制账号登录QQ、TIM
- Illegal key size or default parameters
- web网站服务(一)
- android sdk官网帮助文档
- 《深入理解Java虚拟机》-周志明 -第3版-第一章摘记
- EI检索收录的文献如何下载全文?
- 【电子学会】2022年03月图形化三级 -- 二进制像素绘制程序
- vba能运行在linux上吗,如何运行VBA代码?其实很简单
热门文章
- 2引擎帮助文档_使用Sentence Transformers和Faiss构建语义搜索引擎
- Halcon——点胶机胶水路径应用(2)
- 【Java】环境变量配置
- Go语言中协程的概念和基本使用
- Android零基础入门第87节:Fragment添加、删除、替换
- 【李宏毅2020 ML/DL】P115-117 Actor-Critic Sparse Reward Imitation Learning
- 【李宏毅2020 ML/DL】P53-55 Conditional Generation by RNN Attention Pointer Network Recursive
- 【数据结构笔记01】什么是数据结构
- 窄脉冲matlab实现,[求助]如何获得与50Hz工频同步的窄脉冲信号
- Javascript基础系列之(五)条件语句(if条件语句)