目录
1.Thread类概念
2.Thread的常见构造方法
3.Thread的几个常见属性
4.启动一个线程—start( )
5.中断一个线程
1.使用自定义的变量来作为标志位
2.使用interrupt()
3.观察标志位是否被清除
6.等待一个线程-join()
7.获取当前线程引用
8.休眠当前线程
1.Thread类概念
Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。
每个执行流,也需要有一个对象来描述,类似下图所示,而 Thread 类的对象 就是用来描述一个线程执行流的, JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理。
2.Thread的常见构造方法
方法 | 说明 |
Thread() | 创建线程对象 |
Thread(Runnable target) | 使用 Runnable 对象创建线程对象 |
Thread(String name) | 创建线程对象,并命名 |
Thread(Runnable target, String name) | 使用 Runnable 对象创建线程对象,并命名 |
Thread(ThreadGroup group, Runnable target) | 线程可被用来分组管理,分好的组为线程组, |
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");
3.Thread的几个常见属性
属性 | 获取方法 |
ID | getId() |
名称 | getName() |
状态 | getState() |
优先级 | getPriority() |
是否后台线程 | isDaemon() |
是否存活 | isAlive() |
是否被中断 | isInterrupted() |
注意:
ID 是线程的唯一标识,不同线程不会重复
名称是各种调试工具用到
状态表示线程当前所处的一个情况,之后我会进一步说明
关于后台线程,需要记住一点: JVM会在一个进程的所有非后台线程结束后,才会结束运行。
是否存活,即简单的理解,为 run 方法是否运行结束了
线程的中断问题,下面我们进一步说明
4.启动一个线程—start( )
之前我们已经看到了如何通过覆写 run 方法创建一个线程对象,但线程对象被创建出来并不意味着线程就开始运行了。
覆写 run 方法是提供给线程要做的事情的指令清单
创建线程对象就可以认为是把张三、李四喊过来了
而调用start()方法则就是喊一声,行动起来!现成才去真正独立的执行了
5.中断一个线程
李四一旦进到工作状态,他就会按照行动指南上的步骤去进行工作,不完成是不会结束的。但有时我们需要增加一些机制,例如老板突然来电话了,说转账的对方是个骗子,需要赶紧停止转账,那张三该如 何通知李四停止呢?这就涉及到我们的停止线程的方式了。
目前常见的有以下两种方式:
1.使用自定义的变量来作为标志位
需要给标志位上加 volatile 关键字(保证内存可见性)
public class InterruptTest {public static volatile boolean isQuit = false;static class RunnableT implements Runnable {@Overridepublic void run() {while (!isQuit) {System.out.println(Thread.currentThread().getName()+ "张三正在操作转账!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("及时中断转账!");}}public static void main(String[] args) throws InterruptedException {RunnableT runnableT = new RunnableT();Thread thread = new Thread(runnableT);thread.start();Thread.sleep(3000);System.out.println("老板来电话,得知对面是骗子!");isQuit = true;}
}
运行结果:
2.使用interrupt()
Thread内部包含了一个boolean类型的变量作为线程是否被中断的标记
方法 | 说明 |
public void interrupt() | 中断对象关联的线程,如果线程正在阻塞,则以异常方式通知, 否则设置标志位 |
public static boolean interrupted() | 判断当前线程的中断标志位是否设置,调用后清除标志位 |
public boolean isInterrupted() | 判断对象关联的线程的标志位是否设置,调用后不清除标志位 |
public class InterruptTest {public static volatile boolean isQuit = false;static class RunnableT implements Runnable {@Overridepublic void run() {while (!Thread.interrupted()) {//while (!Thread.currentThread().isInterrupted()) {System.out.println(Thread.currentThread().getName()+ "正在操作转账!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();break;}}System.out.println("及时中断转账!");}}public static void main(String[] args) throws InterruptedException {RunnableT runnableT = new RunnableT();Thread thread = new Thread(runnableT,"张三:");thread.start();Thread.sleep(3000);System.out.println("老板来电话,得知对面是骗子!");thread.interrupt();}
}
运行结果:
thread 收到通知的方式有两种:
1. 如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通知,清除中断标志
当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择忽略这个异常, 也可以跳出循环结束线程
2. 否则,只是内部的一个中断标志被设置,thread 可以通过Thread.interrupted() 判断当前线程的中断标志被设置,清除中断标志,Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志
这种方式通知收到的更及时,即使线程正在 sleep 也可以马上收到。
3.观察标志位是否被清除
interrupt()方法:表示可以中断线程,实际上只是给线程设置一个中断标志,但是线程依旧会执行。
interrupted()方法:Thread类的静态方法。检查当前线程的中断标志,返回一个boolean并清除中断状态,其连续两次调用的返回结果不一样,因为第二次调用的时候线程的中断状态已经被清除,会返回一个false。
isInterrupted()方法:测试线程是否被中断,不会清除中断状态。
使用Thrad.interrupted(),线程中断会清除标志位
public class InterruptTest2 {private static class MyRunnable implements Runnable {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.interrupted());}}}public static void main(String[] args) throws InterruptedException {MyRunnable target = new MyRunnable();Thread thread = new Thread(target, "李四");thread.start();thread.interrupt();}
}
只有一开始是true,后面都是false,因为标志位被清除
使用Thread.currentThread().isInterrupted(),线程中断标记位不会被清除
public class InterruptTest2 {private static class MyRunnable implements Runnable {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().isInterrupted());}}}public static void main(String[] args) throws InterruptedException {MyRunnable target = new MyRunnable();Thread thread = new Thread(target, "李四");thread.start();thread.interrupt();}
}
全部都是true,因为标志位没有被清除
6.等待一个线程-join()
有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。这时我们需要一个方法明确等待线程的结束。
public class JoinTest {public static void main(String[] args) throws InterruptedException {Runnable target = () -> {for (int i = 0; i < 10; i++) {try {System.out.println(Thread.currentThread().getName()+ ": 我还在工作! ");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() + ": 我结束了! ");};Thread thread1 = new Thread(target, "李四");Thread thread2 = new Thread(target, "王五");System.out.println("先让李四开始工作");thread1.start();thread1.join();System.out.println("李四工作结束了,让王五开始工作");thread2.start();thread2.join();System.out.println("王五工作结束了");}
}
附录
方法 | 说明 |
public void join() | 等待线程结束 |
public void join(long millis) | 等待线程结束,最多等 millis 毫秒 |
public void join(long millis, intnanos) | 同理,但可以更高精度 |
7.获取当前线程引用
方法 | 说明 |
public static Thread currentThread(); | 返回当前线程对象的引用 |
8.休眠当前线程
也是我们比较熟悉一组方法,有一点要记得,因为线程的调度是不可控的,所以,这个方法只能保证实 际休眠时间是大于等于参数设置的休眠时间的。
方法 | 说明 |
public static void sleep(long millis) throws InterruptedException | 休眠当前线程 millis 毫秒 |
public static void sleep(long millis, intnanos) throws InterruptedException | 可以更高精度的休眠 |