常见方法。
方法名 |
功能说明 |
注意 |
start() |
启动新线程 在新线程中运行 run 方法 |
start 方法只是让线程进入就绪状态,里面的代码不一定立刻执行。 每一个线程对象的 start 只能调一次,否则会报错。 |
run() |
新线程启动后调用的方法 |
如果在构造 Thread 对象时传递了 Runnable 参数,则线程启动后会调用 Runnable 中的 run 方法。 否则默认不执行任何操作 |
join() |
等待线程结束 |
|
join(long n) |
等待线程运行结束 最多等待 n 毫秒 |
|
getId() |
获取线程长整型的 id |
id 唯一 |
getName() |
获取线程名 |
|
setName(String) |
修改线程名 |
|
getPriority() |
获取线程优先级 |
|
setPriority(int) |
修改线程优先级 |
java 中规定线程优先级是 1 - 10 较大的优先级能提高该线程被 CPU 调度的几率 |
getState() |
获取线程状态 |
Java 中线程状态用 6 个 enum 表示 NEW、RUNNABLE、BLOCKRD、WAITING、TIMED_WAITING、TERMINATED |
isInterrupted() |
判断线程是否被打断 |
不会清楚打断标记 |
isAlive() |
线程是否存活(还没有运行完毕) |
|
interrupt() |
打断线程 |
如果打断的线程正在 sleep、wait、join 会导致被打断的线程抛出 InterruptedException,并清除打断标记 如果打断的正在运行的线程,则会设置打断标记 park 的线程被打断,也会设置打断标记 |
interrupted() |
static 判断当前线程是否被打断 |
会清除打断标记 |
currentThread() |
static 获取当前正在执行的线程 |
|
sleep(long n) |
static 让当前线程休眠 n 毫秒 |
|
yield() |
static 提示线程调度器让出当前线程对 CPU 的使用 |
主要是为了测试和调试 |
stop() |
过时方法 停止线程运行 |
|
suspend() |
过时方法 挂起(暂时)线程运行 |
|
resume() |
过时方法 恢复线程运行 |
|
start 与 run
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.redisc;
import lombok.extern.slf4j.Slf4j;
@Slf4j(topic = "c.Test") public class Test { public static void main(String[] args) { Thread t1 = new Thread("t1") { @Override public void run() { log.debug("runing...");
} };
t1.run(); } }
|
输出
1
| 21:41:03.354 [main] DEBUG c.Test - runing...
|
可以发现调用的主体是 main
而不是 t1
,这种调用,只是普通方法的执行。
如果改成 start
的话,则会输出
1
| 21:44:03.663 [t1] DEBUG c.Test - runing...
|
线程状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.redisc;
import lombok.extern.slf4j.Slf4j;
@Slf4j(topic = "c.Test") public class Test { public static void main(String[] args) { Thread t1 = new Thread("t1") { @Override public void run() { log.debug("runing..."); } }; System.out.println(t1.getState()); t1.start(); System.out.println(t1.getState()); } }
|
输出
1 2 3
| NEW RUNNABLE 21:45:20.461 [t1] DEBUG c.Test - runing...
|
sleep 和 yield
sleep
让当前线程从 Running
进入 Timed Waiting
状态「就绪」
查看 sleep
的状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package com.redisc;
import lombok.extern.slf4j.Slf4j;
@Slf4j(topic = "c.Test") public class Test { public static void main(String[] args) { Thread t1 = new Thread("t1") { @Override public void run() { log.debug("runing..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }; t1.start(); log.debug("t1 state {}", t1.getState());
try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }
log.debug("t1 state {}", t1.getState()); } }
|
输出
1 2 3
| 21:49:52.759 [t1] DEBUG c.Test - runing... 21:49:52.759 [main] DEBUG c.Test - t1 state RUNNABLE 21:49:53.265 [main] DEBUG c.Test - t1 state TIMED_WAITING
|
sleep 打断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package com.redisc;
import lombok.extern.slf4j.Slf4j;
@Slf4j(topic = "c.Test") public class Test { public static void main(String[] args) { Thread t1 = new Thread("t1") { @Override public void run() { log.debug("runing..."); try { Thread.sleep(1000); } catch (InterruptedException e) { log.debug("wake up"); e.printStackTrace(); } } }; t1.start(); log.debug("t1 state {}", t1.getState());
try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }
log.debug("interrupt..."); t1.interrupt(); } }
|
输出
1 2 3 4 5 6 7
| 21:54:19.895 [main] DEBUG c.Test - t1 state RUNNABLE 21:54:19.895 [t1] DEBUG c.Test - runing... 21:54:20.403 [main] DEBUG c.Test - interrupt... 21:54:20.403 [t1] DEBUG c.Test - wake up java.lang.InterruptedException: sleep interrupted at java.base/java.lang.Thread.sleep(Native Method) at com.redisc.Test$1.run(Test.java:13)
|
TimeUnit 更好的可读性
1 2
| Thread.sleep(1000); TimeUnit.SECONDS.sleep(1);
|
上面两个语句是一样的效果,只不过,下面的可读性更好
yield
调用 yield
会让出当前线程从 Runing
进入 Runnable
就绪状态,然后调度执行其他线程
线程优先级
线程优先级会提示调度器优先调用该线程,但是,它仅仅是一个提示,调度器可以忽略它。
如果 CPU
比较忙,那么,优先级高的线程会获得更多的时间片,但是,CPU
空闲时,优先级几乎没用。
join
先看一个程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package com.redisc;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
@Slf4j(topic = "c.Test") public class Test {
static int r = 0;
public static void main(String[] args) throws InterruptedException { test(); }
private static void test() throws InterruptedException { log.debug("开始"); Thread t1 = new Thread(() -> { log.debug("开始"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("结束"); r = 10; }); t1.start(); log.debug("结束 {}", r); } }
|
输出
1 2 3 4
| 22:09:24.918 [main] DEBUG c.Test - 开始 22:09:24.922 [Thread-0] DEBUG c.Test - 开始 22:09:24.922 [main] DEBUG c.Test - 结束 0 22:09:25.927 [Thread-0] DEBUG c.Test - 结束
|
在 t1
睡眠的时候,主线程已经执行完了。
添加 join
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package com.redisc;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
@Slf4j(topic = "c.Test") public class Test {
static int r = 0;
public static void main(String[] args) throws InterruptedException { test(); }
private static void test() throws InterruptedException { log.debug("开始"); Thread t1 = new Thread(() -> { log.debug("开始"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("结束"); r = 10; }); t1.start(); t1.join(); log.debug("结束 {}", r); } }
|
输出
1 2 3 4
| 22:10:33.217 [main] DEBUG c.Test - 开始 22:10:33.219 [Thread-0] DEBUG c.Test - 开始 22:10:34.222 [Thread-0] DEBUG c.Test - 结束 22:10:34.222 [main] DEBUG c.Test - 结束 10
|
join
就是等待某线程运行结束。
join 的执行开始时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| package com.redisc;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
@Slf4j(topic = "c.Test") public class Test {
static int r = 0;
public static void main(String[] args) throws InterruptedException { test(); }
private static void test() throws InterruptedException { Thread t1 = new Thread(() -> { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } });
Thread t2 = new Thread(() -> { try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } });
long start = System.currentTimeMillis();
t1.start(); t2.start();
t1.join(); t2.join();
long end = System.currentTimeMillis(); log.debug("{}", end - start); } }
|
输出
1
| 22:18:43.010 [main] DEBUG c.Test - 2002
|
这是因为,当 start()
开始的时候,线程就已经开始运行了。虽然, t1.join()
等待 t1
执行完,但是,t2
此时也已经执行了 1
秒。
当然, join
还可以设置时间,但是有一点需要注意,join(time)
。如果 线程执行时间小于 time
,那么线程也会结束,即,设置的 time
是线程运行的最大时长。
interrupt 方法
打断 sleep、wait、join
的线程
打算 sleep
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| package com.redisc;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
@Slf4j(topic = "c.Test") public class Test {
static int r = 0;
public static void main(String[] args) throws InterruptedException { test(); }
private static void test() throws InterruptedException { Thread t1 = new Thread(() -> { try { log.debug("sleep"); TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } });
t1.start(); Thread.sleep(2000); log.debug("interrupt"); t1.interrupt(); Thread.sleep(200); log.debug("打断标记: {}", t1.isInterrupted()); } }
|
输出
1 2 3 4 5 6 7 8 9
| 22:29:59.914 [Thread-0] DEBUG c.Test - sleep 22:30:01.913 [main] DEBUG c.Test - interrupt java.lang.InterruptedException: sleep interrupted at java.base/java.lang.Thread.sleep(Native Method) at java.base/java.lang.Thread.sleep(Thread.java:337) at java.base/java.util.concurrent.TimeUnit.sleep(TimeUnit.java:446) at com.redisc.Test.lambda$test$0(Test.java:20) at java.base/java.lang.Thread.run(Thread.java:832) 22:30:02.114 [main] DEBUG c.Test - 打断标记: false
|
这里注意一下 Thread.sleep(200)
。从流程上来讲,打断 sleep
的线程后,该线程的打断标记应该是 false
,但是,如果去掉睡眠 200
,则会显示 true
。
我的理解如下
首先,打断 sleep
的线程后,该线程的打断标记应该是 false
这句话是对的,之所以出现 true
,是因为还没有来得及设定打断标记,就被输出了。
打断正常运行的线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package com.redisc;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
@Slf4j(topic = "c.Test") public class Test {
static int r = 0;
public static void main(String[] args) throws InterruptedException { test(); }
private static void test() throws InterruptedException { Thread t1 = new Thread(() -> { while (true) { if (Thread.currentThread().isInterrupted()) { log.debug("退出循环"); break; } } }, "t1");
t1.start(); Thread.sleep(2000); log.debug("interrupt"); t1.interrupt(); } }
|
输出
1 2
| 22:34:46.295 [main] DEBUG c.Test - interrupt 22:34:46.298 [t1] DEBUG c.Test - 退出循环
|
为什么有
1 2 3 4
| if (Thread.currentThread().isInterrupted()) { log.debug("退出循环"); break; }
|
判断,是因为,即便是打断线程后,该循环依然会不断的运行。
两阶段终止
打断 park 线程
打断 park
线程不会清空打断状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.redisc;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport;
@Slf4j(topic = "c.Test") public class Test {
public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { log.debug("park"); LockSupport.park(); log.debug("unpark"); }, "t1");
t1.start(); // TimeUnit.SECONDS.sleep(1); // t1.interrupt(); } }
|
如果执行的话,会一直停留在
1
| 23:20:05.252 [t1] DEBUG c.Test - park
|
如果加上打断状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.redisc;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport;
@Slf4j(topic = "c.Test") public class Test {
public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { log.debug("park"); LockSupport.park(); log.debug("unpark"); log.debug("打断状态 {}", Thread.currentThread().isInterrupted()); }, "t1");
t1.start(); TimeUnit.SECONDS.sleep(1); t1.interrupt(); } }
|
输出
1 2 3
| 23:20:59.948 [t1] DEBUG c.Test - park 23:21:00.947 [t1] DEBUG c.Test - unpark 23:21:00.947 [t1] DEBUG c.Test - 打断状态 true
|
另外,LockSupport.park();
只能执行一次,如果想要执行多次,需要在前面使用 Thread.interrupted();
守护线程
默认情况下,java 进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其他非守护线程运行结束,即使守护线程的代码没有执行完,也会强制结束。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package com.redisc;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
@Slf4j(topic = "c.Test") public class Test {
public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { log.debug("开始运行"); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("运行结束"); }, "t1");
t1.setDaemon(true); t1.start(); TimeUnit.SECONDS.sleep(1); log.debug("运行结束"); } }
|
输出
1 2
| 23:30:40.786 [t1] DEBUG c.Test - 开始运行 23:30:41.785 [main] DEBUG c.Test - 运行结束
|