Thread.join的使用
Thread.join是什么
Thread.join的作用是保证线程执行结果的可见性。
package com.gupaoedu.p5;
public class ThreadJonDemo {
private static int x=0;
private static int i=0;
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
//阻塞操作
i = 1;
x = 2;
}
});
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
i = x + 2;
}
});
//两个线程的执行顺序,
t1.start();
t1.join(); //t1线程的执行结果对于t2可见(t1线程一定要比t2线程优先执行) --- 阻塞
t2.start();
Thread.sleep(1000);
System.out.println("result:"+i);
}
}
Thread.join的原理
Thread.join的本质其实就是wait/notifyall。它会使当前线程处于等待状态,直到调用join方法的线程执行完毕,当前线程才会被唤醒。
Thead.join的源码
// 源码
if (millis == 0L) {
while(this.isAlive()) {
this.wait(0L);
}
}
notifyall唤醒方法是在JVM底层实现的,所以看不到源码,当线程执行结束后,JVM底层会调用notifyall唤醒等待状态的线程。
Thread.sleep的作用
Thread.sleep是什么
使线程暂停执行一段时间,直到等待的时间结束才恢复执行或在这段时间内被中断。
Thread.sleep的工作流程
- 挂起线程并修改其状态;
- 用sleep()提供的参数来设置一个定时器;
- 当时间结束,定时器会触发,内核收到中断后修改线程的运行状态。线程会被标志为就绪状态等待调度。
package com.gupaoedu.p5;
public class SleepDemo extends Thread{
public static void main(String[] args) {
new SleepDemo().start();
}
@Override
public void run() {
System.out.println("begin:"+System.currentTimeMillis());
try {
Thread.sleep(3000); //睡眠3秒
System.out.println("end:"+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
睡眠的时间一定是大于等于设置的时间,因为睡眠时间结束后线程不会立即运行,需要等到CPU进行调度。
wait和notify的使用
wait和notify的作用
多个线程在处理同一个资源时,可以用到wait和notify来实现线程间的通信。因为多个线程处理同一个资源,所以需要配合synchronized来使用。
wait():让线程处于等待状态。这时线程会释放锁,并存入到线程池中。
notify():唤醒线程池中的第一个线程。
Thread.interrupt和Thread.interrupted
interrupt方法的作用
当其他线程通过调用当前线程的interrupt方法,表示向当前线程打个招呼,告诉他可以中断线程了,至于什么时候中断,取决于线程自己。
如何正确终止一个线程
让线程正确的终止就是等线程运行完run方法,这是一种友好的方式,而不是直接把线程杀掉。
正确的方式:
Thread.interrupted
Thread.interrupt
错误的方式:
Thread.stop 类似于 kill -9
以下代码如何让线程终止?
public class StopDemo {
public static void main(String[] args) {
Thread t1 = new Thread(new StopThread());
t1.start();
}
static class StopThread implements Runnable{
@Override
public void run() {
while (true){
System.out.println("持续运行!!!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
使用interrupt方法
public static void main(String[] args) {
Thread t1 = new Thread(new StopThread());
t1.start();
t1.interrupt(); // 设置中断标识为 true
}
static class StopThread implements Runnable{
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()){ // 判断中断标识,默认为false
System.out.println("持续运行!!!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程的run方法不能正常执行完成有2种情况,死循环和阻塞状态下。以上是死循环的解决方式,阻塞状态下中断线程的方式如下:
public static void main(String[] args) {
Thread t1 = new Thread(new StopThread());
t1.start();
t1.interrupt();
}
static class StopThread implements Runnable{
@Override
public void run() {
try {
// 当线程处于阻塞状态时,调用该线程的interrupt方法会使线程抛出InterruptedException异常
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
当线程在阻塞状态时,调用该线程的interrupt方法会唤醒该线程并抛出InterruptedException异常来响应客户端的中断请求。
interrupted方法的作用
Thread.interrupted()对设置中断标识的线程复位,并且返回当前的中断状态。
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
while (true){
// true 表示被中断过
if(Thread.currentThread().isInterrupted()){
System.out.println("before:" + Thread.currentThread().isInterrupted());
Thread.interrupted(); // 对中断标识复位false,并返回当前的中断状态
System.out.println("after:" + Thread.currentThread().isInterrupted());
}
}
});
thread.start();
TimeUnit.SECONDS.sleep(1);
thread.interrupt(); // 设置中断标识为true
}
执行结果:
before:true
after:false