多线程2
- 2.0 Thread类是什么?
- 2.1 Thread的常见构造方法
- 2.2 Thread的常见属性
- 2.3 start()_启动一个线程
- 2.4 线程中断
- 2.4.1 通过共享标记
- 2.4.2 调用interrupt()方法
- 2.4.3 interrupt()、interrupted()、isInterrupted()
- 小结
- 2.5 join()_等待一个线程
- 2.6 获取当前线程引用
- 2.7 线程休眠
- 2.8 其他方法
- 2.9 补充
1.多线程
2.0 Thread类是什么?
答:Thread 类是线程的描述类,每个线程都有一个 Thread 对象与之关联。
详述:
- Thread 类是 JVM 用来管理线程的一个类。
- 每个线程都有一个唯一的 Thread 对象与之关联。
2.1 Thread的常见构造方法
方法 | 说明 |
Thread() | 创建线程对对象 |
Thread(Runnable target) | 使用 Runnable 对象创建线程对象 (target 为描述任务的对象) |
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) {
// new Thread 创建线程
// (1)java内部线程对象的创建
// (2)系统创建线程,比较耗费时间和性能
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() 为线程运行态时所执行任务的方法,如果使用Runnable传入构造创建线程,就执行Runnable对象的run方法。
假设:只调用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 StopThread1 {
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()
方法通知该线程停止运行。 - thread 收到通知的方式有两种:
- 如果线程调用了 wait/join/sleep 等方法而阻塞挂起,则以InterruptedException 异常的形式通知,清除中断标志。
- 否则,只是内部的一个中断标志被设置,thread 可以通过
2.1Thread.interrupted()
判断当前线程的中断标志被设置,清除中断标志。
2.2Thread.currentThread().isInterrupted()
判断指定线程的中断标志被设置,不清除中断标志。
第二种方式通知收到的更及时,即使线程正在 sleep 也可以马上收到。
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 | 判断当前线程的中断标志位是否设置,调用后清除标志位 |
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()); //一次true 后全是false
//第二次测试
//返回当前中断标志位 不做任何修改
//System.out.println(this.isInterrupted()); //一直是true
}
}
}
public static void main(String[] args) throws InterruptedException {
Worker worker = new Worker();
worker.start(); // t.isInterrupted=false
worker.interrupt(); //中断 t.isInterrupted=true
}
}
第一次运行结果:调用 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) | 同理,但精度更高 |
public class MultiThreadFinishThenDoMain {
public static void main(String[] args) throws InterruptedException {
Thread threads[] = new Thread[10];
for(int i = 0; i < 10;i++){
final int j =i;
Thread t =new Thread(()->{
System.out.println(j);
});
t.start();
threads[i] = t;
}
for(int i = 0;i <10;i++){
threads[i].join();
// 先执行子线程 后执行mian线程
}
System.out.println("main");
}
}
结果:随机打印 0到9 ,后立刻打印 main
2.6 获取当前线程引用
方法 | 说明 |
public static | 返回当前线程对象的引用 |
2.7 线程休眠
作用:让当前线程,休眠给定的时间。
注意:因为线程的调度是不可控的,所以,这个方法只能保证线程真正休眠时间是大于等于休眠时间的。
方法 | 说明 |
public tatic | 休眠当前线程 millis 毫秒 |
public static | 同理,但休眠的时间精确度更高 |
2.8 其他方法
方法 | 说明 |
public static | 返回当前线程的thread group及其子组中活动线程数的估计 (线程+线程创建的子线程存活的估计数量) |
public final void setDaemon(boolean on) | 将此线程标记为 daemon线程或用户线程 (true ,将此线程标记为守护线程 ) |
public static | 使当前线程让出CPU 当前线程由运行态变为就绪态 |
守护线程/后台线程:非用户线程的线程为守护线程。无论守护线程是否存在,没有用户线程存活的进程会自动退出。
2.9 补充
java中的内存区域
栈 每个线程都有自己独立的栈空间
栈里的数据不共享
堆 大家共享的是同一个堆(包括堆里的常量池)和方法区
方法区 堆和方法区中的数据是共享的