《重新学习多线程》 -- Java创建线程 以及 常用API

120 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

创建和运行线程

方式一:直接使用Thread

carbon (13).png

方式2:使用 Runnable 配合 Thread

把【线程】和【任务】(要执行的代码)分开

  • Thread 代表线程

  • Runnable 可运行的任务(线程要执行的代码)

carbon (14).png

carbon (15).png

原理之 Thread 和 Runnable的区别

使用Runnable创建线程时,会调用

image.png 而init方法内部会使成员变量target 为当前的runnable

image.png 当调用Start方法时,会调用run方法。而Thread方式启动线程就是重写run方法

image.png

小结

  • 方法1 是把线程和任务合并在了一起,方法2 是把线程和任务分开了

  • 用 Runnable 更容易与线程池等高级 API 配合

  • 用 Runnable 让任务类脱离了 Thread 继承体系,更灵活

方式3:FutureTask 配合 Thread

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

carbon (16).png

查看线程

线程运行效果

carbon (17).png 观看打印结果可以发现 t1 和 t2 是无规则交替执行

查看进程

windows

  • 任务管理器可以查看进程和线程数,也可以用来杀死进程
  • tasklist 查看进程 tasklist | findstr java
  • taskkill 杀死进程

linux

  • ps -ef ps -ef | grep java
  • kill + pid
  • top
查看线程
top -H -p [pid]

Java

  • jps 命令查看所有Java进程
  • jstack [PID] 查看某个Java进程(PID)的所有线程状态
  • jconsole 来查看某个java进程中线程的运行情况

线程运行原理

栈和栈帧 Java Virtual Machine Stacks (Java 虚拟机栈)

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

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

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

image.png

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

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

  • 线程的 cpu 时间片用完

  • 垃圾回收

  • 有更高优先级的线程需要运行

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

当 Context Switch 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java 中对应的概念

就是程序计数器(Program Counter Register),它的作用是记住下一条 jvm 指令的执行地址,是线程私有的

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

  • Context Switch 频繁发生会影响性能

线程API

image.png

image.png

注意:直接调用线程的run方法,不会开启一个新的线程,而是在当前线程内部执行

getState()

carbon (19).png

sleep 与 yield

sleep

  1. 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞) carbon (20).png
  2. 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException

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

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

yield

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

  2. 具体的实现依赖于操作系统的任务调度器

线程优先级

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

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

join

carbon (11).png

join(long time)

carbon (13).png

interrupt 方法

打断抛出异常

carbon (14).png

打断不抛出异常

carbon (15).png