如果要问我Java当中最难的部分是什么?最有意思的部分是什么?最多人讨论的部分是什么?那我会毫不犹豫地说:多线程 。
Java多线程说它难,也不难,就是有点绕;说它简单,也不简单,需要理解的概念很多,尤其是很多底层知识,如数据结构、操作系统的部分 。在了解多线程之前,最好先知道什么是并发,什么是并行 。不然很容易迷糊 。
Java多线程掌握得好,不仅仅只是对Java , 对任何其他具有并发特性的编程语言,甚至是操作系统,都能有更全面和准确的认识 。
Java多线程最大的特点,而且也是唯一确定的一件事,那就是:在多线程环境下,程序的运行结果是无法预料的,但这也正是它最有趣的地方 。
总的来说,就是这样:
并行:同一时刻可以同时发生/执行多个任务 。学习多线程最好从如下六个方面循序渐进(纯粹个人经验和建议 , 可无视):
并发:同一时刻只能发生/执行一个任务 。
1、线程生命周期:NEW、RUNNABLE(READY、RUNNING)、BLOCKED、WAITING、TIMED_WAITING、TERMINATED状态Java多线程用一句话总结就是「6类5法」 。
2、关键字:synchronized和volatile
3、线程池:ThreadPoolExecutor
4、锁(AQS):悲观锁/乐观锁、轻量级锁/重量级锁、自旋锁/可重入锁等各种锁
5、CAS:各种原子类
6、并发工具类:ArrayBlockingQueue、CountDownLatch、CyclicBarrier、Semaphore等
所谓「6类」,就是多状态的状态分为这6类:
1、新建(NEW):新创建了一个线程,但还没调用start方法这是线程生命周期的状态变化图:
2、运行(RUNNABLE)
2.1、就绪(ready):运行start方法后,线程位于可运行线程池中,等待被调度
2.2、运行中(RUNNING):就绪的线程获得CPU的时间片就变为运行中
3、阻塞(BLOCKED):线程等待获取锁
4、等待(WAITING):接收事件通知后或系统中断后进入等待
5、超时(TIMED_WAITING):等待指定时间后会自行返回
6、终止(TERMINATED):线程已执行完毕
文章插图
简单来说 , 就是这样:
文章插图
而所谓「5法」就是线程的核心方法是这么5个:
1、wait:当前线程调用锁对象的wait方法,当前线程释放锁,进入等待状态 , 由其他线程接着执行用代码来解释一下会更直观一些 。
2、notify/notifyAll:唤醒任意一个或全部等待的线程后接着执行,但并不释放锁
3、join:当前线程调用其他线程的join方法,调用后当前线程进入等待状态
4、yield:当前线程调用,调用后暂停执行(可能无效),变为就绪态
5、sleep:当前线程调用,调用后进入TIME_WAITING状态
第一种wait/notify的情况:
public static void main(String[] args) {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {synchronized ("锁") {System.out.println("t1 start");try {// t1释放锁"锁".wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("t1 end");}}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {synchronized ("锁") {System.out.println("t2 start");try {// 通知t1进入等待队列"锁".notify();} catch (Exception e) {e.printStackTrace();}System.out.println("t2 end");}}});t1.start();t2.start();}此时代码执行流程(两种可能):
1、T1先执行【一 Java多线程-线程生命周期】这里要强调的重点是:
1.1、T1启动,wait让出锁,让出CPU,T2获得CPU , T2启动 , notify了通过object锁等待的线程
1.2、T1被唤醒后等待启动,T2继续执行,T2执行完,T1获得CPU后继续执行
2、T2先执行
2.1、T2执行完,T1启动,让出CPU , 由于没有线程再来执行notify , 程序无限期等待
1、wait会让出CPU而notify不会如果说只有两个线程的时候,还能尝试着分析一下结果,那么当有四个线程的时候会如何呢?看看代码:
2、wait重点在通知其它同用一个object的线程“我暂时不用了”,并且让出CPU
3、notify重点在于通知使用object的对象“我用完了!”
public static void main(String[] args) {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {synchronized ("锁") {System.out.println("t1 start");try {// t1释放锁"锁".wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("t1 end");}}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {synchronized ("锁") {try {System.out.println("t2 start");// 随机通知一个等待的线程进入等待队列"锁".notify();System.out.println("t2 end");} catch (Exception e) {e.printStackTrace();}}}});Thread t3 = new Thread(new Runnable() {@Overridepublic void run() {synchronized ("锁") {try {System.out.println("t3 start");// 随机通知一个等待的线程进入等待队列"锁".notify();System.out.println("t3 end");} catch (Exception e) {e.printStackTrace();}}}});Thread t4 = new Thread(new Runnable() {@Overridepublic void run() {synchronized ("锁") {try {System.out.println("t4 start");// t4释放锁"锁".wait();System.out.println("t4 end");} catch (Exception e) {e.printStackTrace();}}}});t1.start();t2.start();t3.start();t4.start();}
推荐阅读
- 创建.NET程序Dump的几种姿势
- 一台虚拟机,基于docker搭建大数据HDP集群
- 支持JDK19虚拟线程的web框架,之二:完整开发一个支持虚拟线程的quarkus应用
- 一加8pro屏幕分辨率是多少_一加8pro屏幕分辨率怎么样
- vivox70pro参数及价格_vivox70pro配置多少钱
- 不会抽烟怎么装会抽烟啊(一个不抽烟的男的突然抽烟)
- 7 Java多线程:JUC(上)
- FlinkSql之TableAPI详解
- 一次 Java log4j2 漏洞导致的生产问题
- 一荤一素成语|关于素的成语