2.Thread类及常见方法
Thread类是JVM用来管理线程的一个类,换句话说,每一个线程都有一个唯一的Thread对象与之关联。
每个执行流都需要有一个对象来描述,类似下图所示,Thread类的对象就是用来描述一个线程执行流的,JVM会将这些Thread对象组织起来,用来进行线程的调度和管理。
2.1 Thread常见的构造方法
2.2 Thread的常用属性
- ID 是线程的唯一标识,不同线程不会重复。
- name 各种调试工具会用到。
- state 表示当前线程所处的情况。
- priority 理论上说优先级高的线程会被先调用到。
- daemon JVM会在一个进程的所有非后台线程结束后才会结束运行。
- alive 线程是否存活,简单理解为 run() 方法是否运行结束了
- interrupted 线程是否被中断,下面我们来说明
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " 我还活着");
Thread.sleep(1 * 1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我即将死去");
}
});
System.out.println(Thread.currentThread().getName()
+ ": ID: " + thread.getId());
System.out.println(Thread.currentThread().getName()
+ ": 名称: " + thread.getName());
System.out.println(Thread.currentThread().getName()
+ ": 状态: " + thread.getState());
System.out.println(Thread.currentThread().getName()
+ ": 优先级: " + thread.getPriority());
System.out.println(Thread.currentThread().getName()
+ ": 后台线程: " + thread.isDaemon());
System.out.println(Thread.currentThread().getName()
+ ": 活着: " + thread.isAlive());
System.out.println(Thread.currentThread().getName()
+ ": 被中断: " + thread.isInterrupted());
thread.start();
while (thread.isAlive()) {}
System.out.println(Thread.currentThread().getName()
+ ": 状态: " + thread.getState());
}
}
2.3 启动一个线程 start()
之前我们了解了如何让通过覆写run()方法来创建一个线程对象,但是创建就不等于启动线程,下面我们来看如何启动线程。
- 覆写run()方法是提供给线程要做的事情的指令清单。
- 线程对象可以认为是李四把王五叫过来了。
- 而调用start()方法线程才真正独立去执行了。
run()方法和start()方法是不同的,启动线程一定要使用run()方法。
2.4 中断一个线程
李四一旦进到工作状态,他就会按照行动指南上的步骤去进行工作,不完成是不会结束的。但有时我们需要增加一些机制,例如老板突然来电话了,说转账的对方是个骗子,需要赶紧停止转账,那张三该如何通知李四停止呢?这就涉及到我们的停止线程的方式了。
目前常见的方式有以下两种:
- 通过共享的标记来通知
- 通过interrupt()方法来中断
示例1
public class ThreadDemo {
private static class MyRunnable implements Runnable {
public volatile boolean isQuit = false;
@Override
public void run() {
while (!isQuit) {
System.out.println(Thread.currentThread().getName()
+ ": 别管我,我忙着转账呢!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()
+ ": 啊!险些误了大事");
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable target = new MyRunnable();
Thread thread = new Thread(target, "李四");
System.out.println(Thread.currentThread().getName()
+ ": 让李四开始转账。");
thread.start();
Thread.sleep(10 * 1000);
System.out.println(Thread.currentThread().getName()
+ ": 老板来电话了,得赶紧通知李四对方是个骗子!");
target.isQuit = true;
}
}
重点说第二种方法:
1.通过Thread对象调用interrupt()方法来使该线程停止运行。
2.thead收到通知的方式有两种
- 如果线程调用了join()/sleep()/wait()等方法挂起阻塞,则以InterruptedException异常的形式通知,清除中断标志。
- 否则只是内部的一个中断表之被设置,Thread可以通过
Thread.interrupted()判断当前线程的中断标志位被设置,清除中断标志位。
Thead.currentThread.isInterrupted()判断指定线程的中断标志位是否被设置,不清除中断标志位。
第二种方式线程收到的通知更及时,即使在sleep()也可以马上收到通知。
示例2
public class ThreadDemo {
private static class MyRunnable implements Runnable {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("通过异常收到了中断情况");
}
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().isInterrupted());
}
}
}
public static void main(String[] args) {
MyRunnable target = new MyRunnable();
Thread thread = new Thread(target, "李四");
thread.start();
thread.interrupt();
}
}
并且中断标志被清。
示例3
public class ThreadDemo {
private static class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.interrupted());
}
}
}
public static void main(String[] args) {
ThreadDemo.MyRunnable target = new ThreadDemo.MyRunnable();
Thread thread = new Thread(target, "李四");
thread.start();
thread.interrupt();
}
}
只有一开始是true,后面都是false,因为中断标志被清了。
示例4
public class ThreadDemo6 {
private static class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().isInterrupted());
}
}
}
public static void main(String[] args) {
ThreadDemo6.MyRunnable target = new ThreadDemo6.MyRunnable();
Thread thread = new Thread(target, "李四");
thread.start();
thread.interrupt();
}
}
因为中断标志位没有被清除,所以全部是true
2.5 等待一个线程join()
有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。例如,张三只有等李四转账成功,才决定是否存钱,这时我们需要一个方法明确等待线程的结束。
public class Thread1 {
private static class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ":我还在工作");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ":我工作结束了");
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable target = new MyRunnable();
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("王五工作结束了");
}
}
试如果把两个 join 注释掉,现象会是怎么样的呢?
2.6 获取当前线程引用
这个方法我们当前已经比较熟悉了
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
}
2.7 休眠当前线程
有一点要记得,因为线程的调度是不可控的,所以,这个方法只能保证休眠时间是大于等于休眠时间的。
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println(System.currentTimeMillis());
Thread.sleep(3 * 1000);
System.out.println(System.currentTimeMillis());
}
}