《弟哥带你学“并发编程/多线程”系列》 - 第一章 JAVA线程

103 阅读3分钟

创建和运行线程

直接使用 Thread

使用 Runnable 配合 Thread

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

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

好处:

  • 方法1 是把线程和任务合并在了一起,方法2 是把线程和任务分开了
  • 用 Runnable 更容易与线程池等高级 API 配合
  • 用 Runnable 让任务类脱离了 Thread 继承体系,更灵活

Java 8 以后可以使用 lambda 精简代码

FutureTask 配合 Thread

使用线程池

后面再写

线程内部函数

start 与 run

在main方法里调用thread.run(),程序会在main线程里执行

而在main里调用thread.start(),则会新开一个线程,在那个线程里运行thread.run()

sleep 与 yield

sleep

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

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

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

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

yield

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

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

线程优先级

  • 线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它
  • 如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲时,优先级几乎没作用

interrupt 方法详解

打断 sleep,wait,join 的线程

打断 sleep 的线程, 会清空打断状态,以 sleep 为例

打断正常运行的线程

打断正常运行的线程,并不会强行停止线程,不会清空打断状态

打断 park 线程

打断 park 线程, 不会清空打断状态

如果打断标记已经是 true, 则 park 会失效

这说明: park仅在打断标记为false的状态下才会阻塞线程



JAVA线程状态

  • NEW 线程刚被创建,但是还没有调用 start() 方法
  • RUNNABLE 当调用了 start() 方法之后,注意,Java API 层面的 RUNNABLE 状态涵盖了 操作系统 层面的【可运行状态】、【运行状态】和【阻塞状态】(由于 BIO 导致的线程阻塞,在 Java 里无法区分,仍然认为是可运行)
  • BLOCKED ,WAITING , TIMED_WAITING 都是 Java API 层面对【阻塞状态】的细分,后面会在状态转换一节详述
  • TERMINATED 当线程代码运行结束

主线程与守护线程

默认情况下,Java 进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束。

可以看到,守护线程并没有完全运行结束,所以在构建Daemon线程时,不能依靠finally块中的内容来确保执行关闭或清理资源的逻辑。

注:

  • 垃圾回收器线程就是一种守护线程
  • Tomcat 中的 Acceptor 和 Poller 线程都是守护线程,所以 Tomcat 接收到 shutdown 命令后,不会等待它们处理完当前请求