方法名 | 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() |
打断线程 | 如果被打断线程正在 sleep ,wait ,join 会导致被打断的线程抛出 InterruptedException ,并清除打断标记;如果打断的正在运行的线程,则会设置打断标记 ;park 的线程被打断,也会设置打断标记 |
|
interrupted() |
static | 判断当前线程是否被打断 | 返回打断装填并清除打断标记 |
yield() |
static | 提示线程调度器让出当前线程对CPU的使用 | 主要是为了测试和调试 |
sleep(long n) |
static | 让当前执行的线程休眠n毫秒,休眠时让出 cpu的时间片给其它线程 |
创建线程的方式
- 直或者继承使用
Thread
- 使用
Runnable
配合Thread
把【线程】和【任务】(要执行的代码)分开Thread
代表线程Runnable
可运行的任务(线程要执行的代码)
FutureTask
配合Thread
FutureTask
能够接收 Callable
类型的参数,用来处理有返回结果的情况
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 构造方法的参数是给线程指定名字,推荐
Thread t1 = new Thread("t1") {
@Override
// run 方法内实现了要执行的任务
public void run() {
log.debug("hello");
}
};
t1.start();
// 创建任务对象
Runnable task2 = () -> log.debug("hello");
// 参数1 是任务对象; 参数2 是线程名字,推荐
Thread t2 = new Thread(task2, "t2");
t2.start();
// 创建任务对象
FutureTask<Integer> task3 = new FutureTask<>(() -> {
log.debug("hello");
return 100;
});
// 参数1 是任务对象; 参数2 是线程名字,推荐
new Thread(task3, "t3").start();
// 主线程阻塞,同步等待 task 执行完毕的结果
Integer result = task3.get();
log.debug("结果是:{}", result);
}
总结:
- 方法1 是把线程和任务合并在了一起
- 方法2 是把线程和任务分开了
- 用 Runnable 更容易与线程池等高级 API 配合
- 用 Runnable 让任务类脱离了 Thread 继承体系,更灵活
sleep 与 yield
sleep
- 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)
- 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException
- 睡眠结束后的线程未必会立刻得到执行
- 建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性
yield
- 调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程
- 具体的实现依赖于操作系统的任务调度器
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 - 结束
**同步与异步**<br />以调用方角度来讲,如果需要等待结果返回,才能继续运行就是同步;不需要等待结果返回,就能继续运行就是异步 <br /> 
- **案例**
使用join实现线程间的同步
```java
static int r1 = 0;
static int r2 = 0;
private static void test2() throws InterruptedException {
Thread t1 = new Thread(() -> {
sleep(1);
r1 = 10;
});
Thread t2 = new Thread(() -> {
sleep(2);
r2 = 20;
});
t1.start();
t2.start();
long start = System.currentTimeMillis();
log.debug("join begin");
//等待t1执行结束,需要1s,此时t2也在运行
t1.join();
log.debug("t1 join end");
//等待t2执行结束,还需要1s
t2.join();
log.debug("t2 join end");
long end = System.currentTimeMillis();
log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
}
2021-09-28 13:29:08.819 [main] DEBUG c.TestJoin - join begin
2021-09-28 13:29:09.819 [main] DEBUG c.TestJoin - t1 join end
2021-09-28 13:29:10.830 [main] DEBUG c.TestJoin - t2 join end
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
不足
案例 ```java /* 打断正常睡眠的线程 */ public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
log.info("t1");
sleep(1);
}, "t1");
t1.start();
sleep(0.5);
t1.interrupt();
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
```