并发编程(二)Java线程

并发编程(二)Java线程,第1张

并发编程(二)Java线程 一、创建和运行线程 1. 方法一,直接使用Thread

2. 方法二,使用 Runnable 配合 Thread

java 8 以后可以使用 Lambda 精简代码

3. 方法三,FutureTask 配合 Thread

FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况

二、查看进程线程的方法 1. windows

(1)tasklist 查看进程

 

 (2)taskkill 杀死进程

 

2. linux

3. Java

三、原理之线程运行 1. 栈与栈帧

Java Virtual machine Stacks(Java 虚拟机栈)

我们都知道 JVM 中由堆、栈、方法区所组成。每个线程启动后,虚拟机就会为其分配一块栈内存。

(1)每个栈由多个栈帧(frame)组成,对应着每次方法调用时所占用的内存。

(2)每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法。

栈帧图解

2. 线程上下文切换(Thread Context Switch)

因为以下原因导致 cpu 不再执行当前的线程,转而执行另一个线程的代码

(1)线程的 cpu 时间片用完

(2)垃圾回收

(3)有更高优先级的线程需要运行

(4)线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法

当上下文切换发生时,需要由 *** 作系统保存当前线程的状态,并恢复另一个线程的状态,Java 中对应的概念就是程序计数器,它的作用是记住下一条 jvm 指令的执行地址,是线程私有的。

(1)状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、 *** 作数栈、返回地址等

(2)上下文切换频繁发生会影响性能

四、常用方法

1. start 与 run

(1)直接调用 run 是在主线程中执行了 run,没有启动新的线程

(2)使用 start 是启动新的线程,通过新的线程间执行 run 中的代码

2. sleep 与 yield 2.1 sleep

(1)调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)

(2)其他线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException

(3)睡眠结束后的线程未必会立刻得到执行

(4)建议用 TimeUnit 的 sleep 代替 Thread 的sleep 来获得更好的可读性

  

2.2 yield

(1)调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其他线程

(2)具体的实现依赖于 *** 作系统的任务调度器

2.3 线程优先级

(1)线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它。

(2)如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲,优先级几乎没作用。

2.4 案例 限制对 cpu 的使用

3. join 方法详解 3.1 为什么需要 join

 分析:

(1)因为主线程和线程 t1 是并行执行的,t1 线程需要 1 秒之后才能算出 r = 10

(2)而主线程一开始就要打印 r 的结果,所以只能打印出 r = 0

解决方法:

用 join ,加在 t1.start() 之后即可

3.2 等待多个结果

3.3 有时效的 join

 等待线程运行结束,最多等待 n 毫秒。

4. interrupt 方法详解 4.1 打断 sleep、wait、join 的线程

4.2 打断正常运行的线程

打断正常运行的线程, 不会清空打断状态。

4.3 两阶段终止模式

在一个线程 T1 中如何“优雅”终止线程 T2?

错误思路:

(1)使用线程对象的 stop() 方法停止线程

        stop 方法会真正杀死线程,如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁,其他线程将永远无法获取锁。

(2)使用 System.exit(int) 方法停止线程

        目的仅是停止一个线程,但这种做法会让整个程序都停止。

4.4 打断 park 线程

 

5. 主线程与守护线程

详细可以查看该篇文章:

JUC概述(一)_yirenyuan的博客-CSDN博客

6. 五种状态

(1)初始状态:仅是在语言层面创建了线程对象,还未与 *** 作系统线程关联

(2)可运行状态(就绪状态):指该线程已经被创建(与 *** 作系统线程关联),可以由 CPU 调度执行

(3)运行状态:指获取了 CPU 时间片运行中的状态

        当 CPU 时间片用完,会从 【运行状态 】转换至【可运行状态】,会导致线程的上下文切换

(4)阻塞状态

        ①如果调用了阻塞 API,如 BOI 读写文件,这时候该线程实际不会用到 CPU,会导致线程上下文切换,进入【阻塞状态】。

        ②等 BIO *** 作完毕,会由 *** 作系统唤醒阻塞的线程,转换至【可运行状态】

        ③与【可运行状态】的区别是,对【阻塞状态】的线程来说只要它们一直不唤醒,调度器就一直不会考虑调度它们

(5)终止状态:表示线程已经执行完毕,生命周期已经结束,不会再转换为其他状态。

7. 6种状态

(1)NEW:线程刚被创建,但是还没调用 start() 方法

(2)RUNNABLE:当调用了 start() 方法之后,注意,Java API 层面的 RUNNABLE 状态涵盖了 *** 作系统层面的【可运行状态】、【运行状态】和【阻塞状态】(用于 BIO 导致的线程阻塞,在 Java 里无法区分,仍然认为是可运行)

(3)BLOCKED、WAITING、TIMED_WAITING:都是 Java API 层面对【阻塞状态】的细分,后面会在状态转换详诉。

(4)TERMINATED:当线程代码运行结束。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5676453.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存