Thread类是JVM用来管理线程的一个类,换句话说,每个线程都唯一对应着一个Thread对象.
因此,认识和掌握Thread类弥足重要.
本文将从
等方面来进行具体说明.
1)线程创建
方法1:通过创建Thread类的子类并重写run () 方法
class MyThread extends Thread{
@Override
public void run() {
System.out.println("thread");
}
}
public class Thread1 {
public static void main(String[] args) {
Thread thread = new MyThread();
thread.start();
System.out.println("main");
}
}
方法2:通过创建Runnable接口的实现类并重写run ()方法,传实现类的对象作为构造器
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("thread");
}
}
public class Thread2 {
public static void main(String[] args) {
Thread thread = new Thread(new MyThread());
thread.start();
System.out.println("main");
}
}
方法3:使用匿名类,new类,也就是创建该类的子类
public class Thread3 {
public static void main(String[] args) {
Thread thread = new Thread(){
@Override
public void run() {
System.out.println("thread");
}
};
thread.start();
System.out.println("main");
}
}
方法4:使用匿名内部类,new接口,也就是创建该接口的实现类
public class Thread4 {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("thread");
}
});
thread.start();
System.out.println("main");
}
}
方法5:使用lambda表达式(日常开发中使用最多的形式)
public class Thread5 {
public static void main(String[] args) {
Thread thread = new Thread(()->{
System.out.println("thread");
});
thread.start();
System.out.println("main");
}
}
2)线程中断
顾名思义,也就是让线程停止.
本质上而言,让线程停止,方法就一种 -->执行完线程入口方法(run ()方法).
只不过此处可能是正常执行结束,也可能是因为异常而导致结束.
目前常见的有以下两种方式:
1.使用自定义的变量来作为标志位
2.使用Thread提供的变量来作为标志位
下面来介绍:
方法1.使用自定义的变量来作为标志位
public class ThreadDemo9 {
public static boolean isQuit = false;
public static void main(String[] args) {
// boolean isQuit = false;
Thread t = new Thread(() -> {
while (!isQuit) {
System.out.println("hello t");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t 线程终止");
});
t.start();
// 在主线程中, 修改 isQuit
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
isQuit = true;
}
}
执行结果:
此处代码的逻辑大概就是,主线程sleep ()3秒后,t线程里的循环差不多执行3次(sleep()了3秒),此时isQUit被置为ture,t线程里循环结束.t线程继续向下执行逻辑,最终t线程正常结束.
假设:此处设置的isQuti不是类变量,而是局部变量,可行吗?
结果是显然不行的.那到底是什么原因呢?其实是因为-->lambda表达式存在变量捕获.
变量捕获,只能捕获到由1)final修饰2)实际上final的局部变量.
但是如果该局部变量的值被修改了,那么lamoda表达式就捕获不到该值了.
所以此处该如何做呢? --> 设置成类变量.也就是由static修饰的变量.
因为类变量不受变量捕获的限制.
方法2:使用Thread提供的变量来作为标志位
使用Thread.currentThreadOisInterrupted0 代替自定义标志位.
Thread 内部包含了一个 boolean 类型的变量作为线程是否被中断的标记.
public class ThreadDemo10 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
// currentThread 是获取到当前线程实例.
// 此处 currentThread 得到的对象就是 t
// isInterrupted 就是 t 对象里自带的一个标志位.
while (!Thread.currentThread().isInterrupted()) {
System.out.println("hello t");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 把 t 内部的标志位给设置成 true
t.interrupt();
}
}
原因如下:
首先需要明白interrupt 方法的作用:
1)设置标志位为 true
2)如果该线程正在阻塞中(比如在执行 sleep)此时就会把阻塞状态唤醒.通过抛出异常的方式让 sleep 立即结束.(线程在执行sleep(),join (),wait() 方法时,都会进入阻塞状态)
同时需要注意:
当 sleep 被唤醒的时候, sleep 会自动的把 Thread线程内置的标志位给清空(true -> false).这就导致下次循环,循环仍然可以继续执行了!!!
因此:如果需要结束循环,就得在 catch 中搞个 break.
此处再具体说明一下sleep() 的工作原理:(分3种情况)
1)如果 sleep 执行的时候看到这个标志位是 false --> sleep 正常进行休眠操作.
2)如果当前标志位为 true,sleep 无论是刚刚执行还是已经执行了一半,都会触发两件事 :1.立即抛异常2.清空标志位为 false.
3)如果设置 interrupt 的时候,恰好, sleep 刚醒~~ 这个时候赶巧了,执行到下一轮循环的条件,就直接结束了但是这种概率非常低,毕竟 sleep 的时间已经占据了整个循环体的 99.99999999% 的时间了.几乎可以视为不可能事件.
那可能就又会有人问了,为啥 sleep 要清空标志位呢???
目的就是为了让线程自身能够对于线程何时结束,有一个更明确的控制~
当前interrupt 方法,不是让线程立即结束,而是告诉他,你该结束了,至于他是否真的要结束,立即结束还是等会结束,都是代码来灵活控制的。
interrupt 只是通知,而不是"命令".
为什么这么说呢?原因就是,t线程是否结束,是取决于自身的,也就是说,根据catch里的逻辑实现,可以结束,也可以不结束.
那肯定又会有人问了:那为啥java 这里 不强制设定成命令结束"的操作? 只要调用 interrupt 就立即结束??
主要是设定成这种,非常不友好的~
线程 t 何时结束,一定是 t 自己最清楚~~ 交给 t 自身来决定比较好~~
映射到生活:如果正在跟领导打电话,你的女票让你去帮她拿东西.那此时,肯定是工作优先嘛!!些竟拿东西不是太重要的事,可以先搁置一会!!
3)线程等待
线程之间是并发执行的,操作系统对于线程的调度是无序的.无法判定两个线程谁先执行结束, 谁后执行结束!!!
在某些业务场景下,需要明确规定线程的结束顺序.可以使用线程等待来实现 -->join 方法
public class ThreadDemo {
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("王五工作结束了");
}
}
输出结果:
当前是在main线程里执行的thread1.join()(也就是说是让main线程等待thread1线程).
那么在main线程里执行的thread1.join0 的时候,此时有两种情况:
1)如果 thread线程仍在继续运行,那么main线程就会暂时不参与程调度,等待thread1 线程结束main线程才继续执行代码逻辑.
2)如果thread1线程已经结束,那么main线程也就不用等待了,就直接继续执行自己的代码逻辑了.
但不管是哪种情况,都能保证thread1线程是先于main线程结束的.
4)线程休眠
方法:public static void sleep(long millis) throws InterruptedException
说明:
1)单位是ms
2)sleep是Thread类里的静态方法,通过类名可以直接调用.
3)作用是休眠当前进程.
4)需要用try-catch处理InterruptedException中断异常(意思就是 sleep 睡眠过程中,还没到点就提前唤醒了).
5)获取线程实例
方法:public static Thread currentThread()
说明:在哪个线程里调用,返回的就是哪个线程的对象引用.
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
}
输出结果:main
多提一句:多线程编程在Java中很常见,也很重要.务必掌握!!!
今天不想敲代码,所以才去敲.
uu们加油呀!!