前言
一、启动一个线程 start( )
在上一节我们讲解了如何通过覆写 run 方法创建一个线程对象,但线程对象被创建出来并不意味着线程就开始运行了。比如:
- 覆写 run 方法是提供给线程要做的事情的指令清单
- 线程对象可以认为是把 李四、王五叫过来了
- 而调用 start( ) 方法,就是喊一声:"行动起来!",线程才真正独立去执行了
调用 start 方法, 才真的在操作系统的底层创建出一个线程。
Thread类中run和start的区别:
作用功能不同:
1. run方法的作用是描述线程具体要执行的任务;
2. start方法的作用是真正的去申请系统线程
.运行结果不同:
1. run方法是一个类中的普通方法,主动调用和调用普通方法一样,会顺序执行一次;
2. start调用方法后, start方法内部会调用Java 本地方法(封装了对系统底层的调用)真正的启动线程,并执行run方法中的代码,run 方法执行完成后线程进入销毁阶段
参考示例代码:
bash
/**
* @author Zhang
* @date 2024/4/2915:16
* @Description:
*/
//创建一个类,继承自Thread
class MyThread extends Thread{
//这个方法就是线程的入口方法
@Override
public void run() {
// System.out.println("hello Thread");
while(true){
System.out.println("hello threat");
try {
Thread.sleep(1000); //休眠1000毫秒
/**
* 此处必须try catch,不能throws
* 在这个代码中重写的是父类的run,父类的run没有throws,因此,子类方法也就不能有throws
*/
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
//创建线程
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
Thread t = new MyThread();
//start 和run都是Threat的成员
//run只是描述了线程的入口(线程要做生么任务)
//start 则是真正调用了系统API,在系统中创建线程,让线程再调用run
t.start();
while(true){
System.out.println("hello main");
Thread.sleep(1000); //休眠1000毫秒
}
}
}
二、中断一个线程
停止一个线程主要有以下两种方式:
2.1 通过共享的标记来进行沟通
参考实例代码:
bash
/**
* @author Zhang
* @date 2024/5/415:58
* @Description:
*/
public class Demo8 {
private static boolean isQuit = false; //成员变量
public static void main(String[] args) throws InterruptedException {
// boolean isQuit = false; 不能使用局部 变量
Thread t= new Thread(()->{
while(!isQuit){
//
System.out.println("线程工作中");
//此处的打印可以替换成任意的逻辑来表示逻辑的实际工作内容
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("线程作完毕");
});
t.start();
Thread.sleep(5000);
isQuit = true;
System.out.println("设置 isQuit 为 true");
}
}
2.2 调用interrupt( )方法来通知
使用 Thread.interrupted() 或Thread.currentThread().isInterrupted() 代替自定义标志位。其中,Thread 内部包含了一个 boolean 类型的变量作为线程是否被中断的标记。
使用 thread 对象的 interrupted() 方法通知线程结束的代码参考实例:
bash
public class ThreadDemo {
private static class MyRunnable implements Runnable {
@Override
public void run() {
// 两种方法均可以
while (!Thread.interrupted()) {
//while (!Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName()
+ ": 别管我,我忙着转账呢!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(Thread.currentThread().getName()
+ ": 有内鬼,终止交易!");
// 注意此处的 break
break;
}
}
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()
+ ": 老板来电话了,得赶紧通知李四对方是个骗子!");
thread.interrupt();
}
}
thread 收到通知的方式有两种:
- 如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以InterruptedException 异常的形式,清除中断标志。
当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法, 可以选择忽略这个异常, 也可以跳出循环结束线程
- 否则,只是内部的一个中断标志被设置,thread 可以通过
Thread.interrupted() 判断当前线程的中断标志被设置,清除中断标志
Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志
bash
/**
* @author Zhang
* @date 2024/5/416:27
* @Description:
*/
public class Demo9 {
public static void main(String[] args) {
Thread t = new Thread(() ->{
//Thread类内部,有一个现成的标志位,可以判定当前的循环是否结束
while(!Thread.currentThread().isInterrupted()){
System.out.println("线程工作中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//throw new RuntimeException(e);
//1.假装没听见,循环继续正常执行
e.printStackTrace();
// 2.加上一个break,表示线程立即结束
//break;
//3.先做一些其他工作,完成之后再结束
//其他工作的代码
//break;
}
}
});
t.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("线程终止");
t.interrupt();
}
}
三、 等待一个线程join( )
有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作
参考实例代码:
bash
/**
* @author Zhang
* @date 2024/5/416:54
* @Description:
*/
public class Demo10 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
for (int i = 0; i<5;i++ ){
System.out.println("t 线程工作中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
//让主线程等待t线程执行结束
//一旦调用join,主线程就会出发阻塞。此时t线程就可以趁机完成后续的工作
//一直阻塞到 t 执行完毕了, join才会解除阻塞,才能继续执行
System.out.println("join 等待开始");
t.join();
//t.join 工作过程:
// 1)如果t线程正在运行中,此时调用 join 的线程就会阻塞, 一直阻塞到t线程执行结束为止
// 2)如果t线程已经执行结束了,此时调用 join 线程,就直接返回了,不会涉及到阻塞~~
System.out.println("join 等待结束");
}
}
其他一些 join( )方法
四、获取当前线程引用 和 休眠当前线程
4.1 获取当前线程引用
常见方法:public static Thread currentThread(); ------返回当前线程对象的引用
参考代码实例
bash
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
}
4.2 休眠当前线程
bash
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());
}
}
五、 线程的状态
总结
好啦!以上就是今天要讲的内容,本文介绍了Thread类的start( )方法、线程的的中断、等待一个线程join( )、获取当前线程的引用,休眠当前线程,以及线程的引用。在本栏的下一节我们将会继续介绍Thread中重点内容------线程安全。