方法名 static 功能说明 备注
start() 启动一个新线程,在新的线程运行 run 方法中的代码 start方法只是让线程进入就绪,里面代码不一定立刻运行(没有CPU 的时间片)。每个线程的start方法只能调用一次,调用了多次会出现IllegalThreadStateException
run() 新线程启动后会调用的方法 如果在构造 Thread对象时传递了 Runnable参数,则线程启动后会调用 Runnable中的 run方法,否则默认不执行任何操作。但可以创建 Thread的子类对象,来覆盖默认行为
join() 等待线程运行结束 调用方等待被调用方结束,当前线程调用t1.join()则当前线程等待t1运行结束
join(long n) 等待线程运行结束,最多等待 n毫秒
getId() 获取线程长整型的 id id 唯一
getState() 获取线程状态 Java 中线程状态是用 6 个 enum 表示,分别为:NEW, RUNNABLE, BLOCKED, WAITING,TIMED_WAITING, TERMINATED
isInterrupted() 判断是否被打断 不会清除打断标记
isAlive() 线程是否存活(还没有运行完毕)
interrupt() 打断线程 如果被打断线程正在 sleepwaitjoin会导致被打断的线程抛出 InterruptedException并清除打断标记;如果打断的正在运行的线程,则会设置打断标记 ;park的线程被打断,也会设置打断标记
interrupted() static 判断当前线程是否被打断 返回打断装填并清除打断标记
yield() static 提示线程调度器让出当前线程对CPU的使用 主要是为了测试和调试
sleep(long n) static 让当前执行的线程休眠n毫秒,休眠时让出 cpu的时间片给其它线程

创建线程的方式

  1. 直或者继承使用 Thread
  2. 使用 Runnable配合 Thread

把【线程】和【任务】(要执行的代码)分开
Thread代表线程Runnable可运行的任务(线程要执行的代码)

  1. FutureTask配合 Thread

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

  1. public static void main(String[] args) throws ExecutionException, InterruptedException {
  2. // 构造方法的参数是给线程指定名字,推荐
  3. Thread t1 = new Thread("t1") {
  4. @Override
  5. // run 方法内实现了要执行的任务
  6. public void run() {
  7. log.debug("hello");
  8. }
  9. };
  10. t1.start();
  11. // 创建任务对象
  12. Runnable task2 = () -> log.debug("hello");
  13. // 参数1 是任务对象; 参数2 是线程名字,推荐
  14. Thread t2 = new Thread(task2, "t2");
  15. t2.start();
  16. // 创建任务对象
  17. FutureTask<Integer> task3 = new FutureTask<>(() -> {
  18. log.debug("hello");
  19. return 100;
  20. });
  21. // 参数1 是任务对象; 参数2 是线程名字,推荐
  22. new Thread(task3, "t3").start();
  23. // 主线程阻塞,同步等待 task 执行完毕的结果
  24. Integer result = task3.get();
  25. log.debug("结果是:{}", result);
  26. }

总结:

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

    sleep 与 yield

    sleep
  1. 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)
  2. 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException
  3. 睡眠结束后的线程未必会立刻得到执行
  4. 建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性

yield

  1. 调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程
  2. 具体的实现依赖于操作系统的任务调度器

join方法

  • 案例

不使用join方法方执行结果为0

  • 分析
    • 因为主线程和线程 t1 是并行执行的,t1 线程需要 1 秒之后才能算出 r=10
    • 而主线程启动玩t1后就要打印 r 的结果,所以只能打印出 r=0 ```java static int r = 0;

private static void test1() throws InterruptedException { log.debug(“开始”); Thread t1 = new Thread(() -> { log.debug(“开始”); sleep(1); log.debug(“结束”); r = 10; }); t1.start(); // t1.join(); log.debug(“结果为:{}”, r); log.debug(“结束”); } 2021-09-28 11:30:22.659 [main] DEBUG c.TestJoin - 开始 2021-09-28 11:30:22.696 [Thread-0] DEBUG c.TestJoin - 开始 2021-09-28 11:30:22.696 [main] DEBUG c.TestJoin - 结果为:0 2021-09-28 11:30:22.697 [main] DEBUG c.TestJoin - 结束 2021-09-28 11:30:23.700 [Thread-0] DEBUG c.TestJoin - 结束

  1. **同步与异步**<br />以调用方角度来讲,如果需要等待结果返回,才能继续运行就是同步;不需要等待结果返回,就能继续运行就是异步 <br /> ![image.png](https://cdn.nlark.com/yuque/0/2021/png/22353188/1632800415121-43226bae-7ed0-443b-b647-75c95398948d.png#clientId=u56e36f77-a1d7-4&from=paste&height=339&id=uc80dd15d&margin=%5Bobject%20Object%5D&name=image.png&originHeight=677&originWidth=512&originalType=binary&ratio=1&size=23323&status=done&style=none&taskId=ue353c69a-21f4-4d66-893e-144ac5dface&width=256)
  2. - **案例**
  3. 使用join实现线程间的同步
  4. ```java
  5. static int r1 = 0;
  6. static int r2 = 0;
  7. private static void test2() throws InterruptedException {
  8. Thread t1 = new Thread(() -> {
  9. sleep(1);
  10. r1 = 10;
  11. });
  12. Thread t2 = new Thread(() -> {
  13. sleep(2);
  14. r2 = 20;
  15. });
  16. t1.start();
  17. t2.start();
  18. long start = System.currentTimeMillis();
  19. log.debug("join begin");
  20. //等待t1执行结束,需要1s,此时t2也在运行
  21. t1.join();
  22. log.debug("t1 join end");
  23. //等待t2执行结束,还需要1s
  24. t2.join();
  25. log.debug("t2 join end");
  26. long end = System.currentTimeMillis();
  27. log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
  28. }
  29. 2021-09-28 13:29:08.819 [main] DEBUG c.TestJoin - join begin
  30. 2021-09-28 13:29:09.819 [main] DEBUG c.TestJoin - t1 join end
  31. 2021-09-28 13:29:10.830 [main] DEBUG c.TestJoin - t2 join end
  32. 2021-09-28 13:29:10.830 [main] DEBUG c.TestJoin - r1: 10 r2: 20 cost: 2012
  • 分析
    第一个 join:等待 t1 时, t2 并没有停止, 而在运行
    第二个 join:1s 后, 执行到此, t2 也运行了 1s, 因此也只需再等待 1s
  • 不足

    • 需要外部共享变量,不符合面向对象封装的思想
    • 必须等待线程结束,不能配合线程池使用

      interrupt 方法

      打断 sleepwaitjoin的线程这几个方法都会让线程进入阻塞状态,打断 sleep的线程, 会清空打断状态;打断正常运行的线程, 不会清空打断状态;打断 park线程, 不会清空打断状态
  • 案例 ```java /* 打断正常睡眠的线程 */ public static void main(String[] args) throws InterruptedException {

    1. Thread t1 = new Thread(() -> {
    2. log.info("t1");
    3. sleep(1);
    4. }, "t1");
    5. t1.start();
    6. sleep(0.5);
    7. t1.interrupt();
    8. log.info("t1的打断状态{}", t1.isInterrupted());

    } 2021-09-28 13:46:14.929 [t1] INFO com.song.method.TestInterrupt - t1 java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at com.song.util.Sleeper.sleep(Sleeper.java:8) at com.song.method.TestInterrupt.lambda$main$0(TestInterrupt.java:14) at java.lang.Thread.run(Thread.java:748) 2021-09-28 13:46:15.433 [main] INFO com.song.method.TestInterrupt - t1的打断状态false

/* 打断正常执行的线程 */ public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { while (true) { boolean interrupted = Thread.currentThread().isInterrupted(); if (interrupted) { log.info(“被打断…{}”, interrupted); break; } } }, “t1”); t1.start(); sleep(0.5); t1.interrupt(); } 2021-09-28 13:52:04.707 [t1] INFO com.song.method.TestInterrupt - 被打断…true

/* 打断park线程 */ public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { log.info(“准备park”); //暂停等待 LockSupport.park(); log.debug(“打断状态:{}”, Thread.currentThread().isInterrupted()); }, “t1”); t1.start(); sleep(0.5); t1.interrupt(); //唤醒park线程 LockSupport.unpark(t1); } 2021-09-28 13:55:16.952 [t1] INFO com.song.method.TestInterrupt - 准备park 2021-09-28 13:55:17.463 [t1] DEBUG com.song.method.TestInterrupt - 打断状态:true

```