多线程2
2.0 Thread类是什么?
答:Thread 类是线程的描述类,每个线程都有一个 Thread 对象与之关联。
即:
- Thread 类是 JVM 用来管理线程的一个类。
- 每个线程都有一个唯一的 Thread 对象与之关联。
2.1 Thread的常见构造方法
方法 | 说明 |
---|---|
Thread() | 创建线程对对象 |
Thread(Runnable target) | 使用 Runnable 对象创建线程对象 |
Thread(String name) | 创建线程对象并命名 |
Thread(Runnable target , String name) | 使用 Runnable 对象创建线程对象并命名 |
public class ThreadName {
private static class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
public MyThread() {
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Thread t1 = new MyThread();
Thread t2 = new MyThread("我是AAA");
Thread t3 = new Thread(new MyThread());
Thread t4 = new Thread(new MyThread(), "我是BBB");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
用jconsole观察:
2.2 Thread的常见属性
属性 | 获取方法 | 补充 |
---|---|---|
ID | getId() | 线程的唯一标识,不同线程ID不会重复 |
名称 | getName() | 在调试工具中会用到 |
状态 | getState() | 表示线程当前状态 |
优先级 | getPriority() | 优先级高的线程理论上更容易被调度 |
是否后台线程 | isDaemon() | JVM会在一个进程的所有非后台进程结束后,才结束运行 |
是否存活 | isAlive() | run方法是否运行结束 |
是否中断 | isInterrupted() | 线程是否中断 |
public class ThreadFields {
private static class MyRunnable implements Runnable {
@Override
public void run() {
Thread current = Thread.currentThread();
System.out.println(current.getId());
System.out.println(current.getName());
System.out.println(current.getPriority());
System.out.println(current.getState());
System.out.println(current.isAlive());
/*
不能用this 此时this指的是Runnable对象 不是Thread对象
System.out.println(this.isInterrupted());
System.out.println(this.isDaemon());
*/
}
}
private static class MyThread extends Thread {
@Override
public void run() {
Thread current = Thread.currentThread();
System.out.println(current.getId());
System.out.println(current.getName());
System.out.println(current.getPriority());
System.out.println(current.getState());
System.out.println(current.isAlive());
//可以用this
System.out.println(this.isInterrupted());
System.out.println(this.isDaemon());
}
}
public static void main(String[] args) {
new MyThread().start();
}
}
运行结果:
2.3 start()—启动一个线程
注意:
- 线程对象被创建出来并不意味着线程就开始运行。
- 启动线程必须调用start()。
- run方法和start()方法不同。
假设:只调用run(),不调用start(),结果会是什么?
public class StartOrRun {
private static class MyThread extends Thread {
@Override
public void run() {
while (true) {
System.out.println("MyThread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t = new MyThread();
t.run();
/* 只执行 MyThread 不执行 main
不要调用 run
*/
while (true) {
System.out.println("main");
Thread.sleep(1000);
}
}
}
运行结果:只输出 MyThread 不输出mian,根本达不到多线程应有的效果。所以,线程启动一定要调用start()。
2.4 线程中断
2.4.1 通过共享标记
注意:这种方法有延迟
import javafx.scene.layout.Priority;
import javax.swing.plaf.nimbus.State;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
public class StopThread {
private static class Worker extends Thread {
private volatile boolean quit = false;
//自定义共享标记
Worker() {
super("李四");
}
public void setQuit(boolean quit) {
this.quit = quit;
}
@Override
public void run() {
while (!quit) {
System.out.println(this.getName() + ": 我正在转账,别烦我");
try {
//结果1
Thread.sleep(3000);
//结果2 //Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(this.getName() + ": 对方是骗子,我不转账了");
}
}
public static void main(String[] args) throws InterruptedException {
Worker worker = new Worker();
worker.start();
System.out.println("我是张三,正在等李四转账");
Thread.sleep(10 * 1000);
System.out.println("打听到对方是骗子,通知李四停止转账");
worker.setQuit(true);
System.out.println("通知李四完毕");
worker.join();
System.out.println("李四停止转账");
}
}
运行结果1:因为10不能整除3,所以有延迟(李四不能立刻做出反应)。
运行结果2:因为10/5可以整除,故李四立马做出反应。
2.4.2 调用interrupt()方法
- 调用Thread对象的 Interrupt() 方法,会将对象的 interrupt status 置为 true,时间以型的中断。
- 对于Thread对象关联的线程来说,有两种途径获取该信息:
- 如果当前线程正在 sleep/wait ,会以 InterruptedException 的形式通知。
- 否则,需要自行去判断对象中的 interrupt status 。
2.1 调用对象的 isInterrupted() 方法 —— 判断该对象的 interrupt status 。
2.2调用 Thread.interrupted() 方法 —— 判断的是当前线程对象的 interrupt status 。相当于Thread.currentThread().isInterrupted();
public class StopThread2 {
private static class Worker extends Thread {
Worker() {
super("李四");
}
@Override
public void run() {
while (!this.isInterrupted()) {
System.out.println(this.getName() + ": 我正在转账,别烦我");
try {
Thread.sleep(50 * 1000);
} catch (InterruptedException e) {
System.out.println("我从睡梦中惊醒");
break;
}
}
System.out.println(this.getName() + ": 对方是骗子,我不转账了");
}
}
public static void main(String[] args) throws InterruptedException {
Worker worker = new Worker();
worker.start();
System.out.println("我是张三,正在等李四转账");
Thread.sleep(10 * 1000);
System.out.println("打听到对方是骗子,通知李四停止转账");
worker.interrupt();
System.out.println("通知李四完毕");
worker.join();
System.out.println("李四停止转账");
}
}
运行结果:张三只会等待10s,立即通知李四(调用 interrupt 方法),李四立刻做出响应(无延迟)。
2.4.3 interrupt()、interrupted()、isInterrupted()
方法 | 说明 |
---|---|
public void interrupt() | 中断对象关联的线程,如果线程正在阻塞,则以异常方式通知,否则设置标志位 |
public static boolean interrupted() | 判断当前线程的中断标志位是否设置,调用后清除标志位 |
public boolean isInterrupted() | 判断对象关联的线程的标志位是否设置,调用后不清除标志位 |
-
interrupt()
注意:如果是从catch (InterruptedException e) { System.out.println(this.isInterrupted()); System.out.println("我从睡梦中惊醒"); break; }
分支跳出的,this.isInterrupted()
依然打印的是false。 -
interrupted()
-
isInterrupted()
测试:
public class StopThread3 {
private static class Worker extends Thread {
Worker() {
super("李四");
}
@Override
public void run() {
while (true) {
//第一次测试
System.out.println(Thread.interrupted()); //一次false 后全是true
//第二次测试
//System.out.println(this.isInterrupted()); //一直是true
}
}
}
public static void main(String[] args) throws InterruptedException {
Worker worker = new Worker();
worker.start();
worker.interrupt(); //中断
}
}
第一次运行结果:调用 Thread.interrupted() ,第一次输出 true ,后一直输出 false 。
第二次运行结果:调用 this.isInterrupted() ,一直输出 true 。
小结
- InterruptedException 被抛出,则interrupt status 被重置(=false)
- Thread.interrupted() 首次返回真正的线程状态,然后 interrupt status = false
- this.isInterrupted() 不会重置
测试:
public class InterruptThread {
private static class MyThread1 extends Thread {
@Override
public void run() {
while (true) {
try {
Thread.sleep(10_000_000);
} catch (InterruptedException e) {
// 通过异常的方式通知中断 我收到了中断通知
System.out.println(isInterrupted()); //false
break;
}
}
System.out.println("退出");
}
}
private static class MyThread2 extends Thread {
@Override
public void run() {
while (!isInterrupted()) {
}
// 通过判断中断状态退出,状态不会被重置
System.out.println(isInterrupted()); //true
System.out.println("退出");
}
}
private static class MyThread3 extends Thread {
@Override
public void run() {
while (!Thread.interrupted()) {
}
// 通过判断中断状态退出,状态被重置
System.out.println(isInterrupted()); //false
System.out.println("退出");
}
}
public static void main(String[] args) {
MyThread1 t1 = new MyThread1();
t1.start();
t1.interrupt();
MyThread2 t2 = new MyThread2();
t2.start();
t2.interrupt();
MyThread3 t3 = new MyThread3();
t3.start();
t3.interrupt();
}
}
运行结果:
2.5 join()—等待一个线程
方法 | 说明 |
---|---|
public void join() | 等待线程结束 |
public void join(long millis) | 等待线程结束,最多等待 millis 毫秒 |
public void join(long millis, int nanos) | 同理,但精度更高 |
2.6 获取当前线程引用
方法 | 说明 |
---|---|
public static Thread currentThread() | 返回当前线程对象的引用 |
2.7 线程休眠
注意:因为线程的调度是不可控的,所以,这个方法只能保证线程休眠时间是大于等于休眠时间的。
方法 | 说明 |
---|---|
public static void sleep(long millis) throws InterruptedException | 休眠当前线程 millis 毫秒 |
public static void sleep(long millis, int nanos) throws InterruptedException | 同理,但休眠的时间精确度更高 |
2.8 补充
java中的内存区域
栈 每个线程都有自己独立的栈空间
栈里的数据不共享
堆 大家共享的是同一个堆(包括堆里的常量池)和方法区
方法区 堆和方法区中的数据是共享的