认识join
简单理解: 被创建出来的线程t1, 如果t1.join()
到主线程,那么需要等到t1线程执行完成,主线程才能继续执行.
public class ThreadJoin {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
IntStream.range(1, 1000)
.forEach(i -> System.out.println(Thread.currentThread().getName() + "->" + i));
});
Thread t2 = new Thread(() -> {
IntStream.range(1, 1000)
.forEach(i -> System.out.println(Thread.currentThread().getName() + "->" + i));
});
//必须先启动,然后才能join
t1.start();
t2.start();
//这里t1和t2都是join到主线程(main)
//所以t1 和t2 还是同时执行的,
t1.join();
t2.join();
//当t1和t2执行完成,才能继续执行下面的代码
Optional.of("All of tasks finish done.").ifPresent(System.out::println);
IntStream.range(1, 1000)
.forEach(i -> System.out.println(Thread.currentThread().getName() + "->" + i));
}
}
上述代码中t1和t2交替执行输出,执行完成,才是main线程输出
当然可以设置等待超时时间
t1.start();
//主线程等待t1线程先执行100毫秒,20纳秒之后,如果t1还没有执行完成, //主线程和t1一起并发执行
t1.join(100,10);
可以使用join实现主线程的永不退出
Thread.currentThread().join();
认识demoan
简单认识: 由父线程创建出来的线程,在启动之前设置为 t.setDaemon(true);
父线程退出,该后台线程t也会跟着退出
public class DaemonThread {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " running");
Thread.sleep(100000);
System.out.println(Thread.currentThread().getName() + " done.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t.setDaemon(true);
t.start();
//父线程五秒之后退出,创建的t 守护线程会一起退出
Thread.sleep(5_000);
System.out.println(Thread.currentThread().getName());
}
}
输出:
Thread-0 running
main
父线程等待五秒之后结束,t线程也会跟着主线程一起结束.
认识interrupt(),方法
简单认识: 给线程标记一个打断的标志,并不是结束线程,当线程被sleep(),wait(),join()
会抛出 InterruptedException
异常,抛出之后,该线程的打断标志复原
public class InterruptTest {
public static void main(String[] args) {
Thread t1 = new Thread(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("收到标志>>>> 线程被打断");
System.out.println("异常抛出后t1线程打断标志>>>>" + Thread.interrupted());
e.printStackTrace();
}
});
t1.start();
//保证t1线程已经在执行
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//输出打断前的标志
System.out.println("t1打断前的标志>>>>>" + t1.isInterrupted());
//打断线程
t1.interrupt();
}
}
输出:
t1打断前的标志>>>>>false
java.lang.InterruptedException: sleep interrupted
收到标志>>>> 线程被打断
at java.lang.Thread.sleep(Native Method)
异常抛出后t1线程打断标志>>>>false
注意:一但异常抛出,打断标志会复原
对t.join() 的时候,打断的应该是父线程
//创建一个线程
Thread t = new Thread() {
@Override
public void run() {
while (true) {
}
}
};
//开启线程
t.start();
//保存一下父线程
Thread main = Thread.currentThread();
//开启第二个线程,在里面打断join方法上的线程
Thread t3 = new Thread() {
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//打断主线程
main.interrupt();
System.out.println("interrupt");
}
};
t3.start();
try {
//注意t.join,其实执行方法还是主方法
t.join();
} catch (InterruptedException e) {
System.out.println("收到打断信号>>>");
e.printStackTrace();
}
System.out.println("main线程继续执行");
}
输出:
java.lang.InterruptedException
interrupt
收到打断信号>>>
main线程继续执行
原因: t.join()到父线程是main线程. 需要对main线程打断,才能捕获到打断异常
使用这三个方法,合理的结束线程
场景: 如果一个任务数据库查询处理数据,在规定的时间内不能完成,需要结束这个线程.
在主线程中(可以理解为main线程)创建一个执行线程executeThread,在执行线程中在创建一个daemon后台线程runnerThread,去执行真正的任务.将
runnerThread.join()到父线程中,当一定时间后,处理没有完成,执行executeThread.interrupt().打断执行线程,捕获到异常,执行线程就可以往下执行,不需要等待runnerThread()执行完成.
public class ThreadService {
//执行线程
private Thread executeThread;
//判断有没有在规定时间内结束
private volatile boolean finished = false;
public void execute(Runnable task) {
executeThread = new Thread() {
@Override
public void run() {
Thread runner = new Thread(task);
runner.setDaemon(true);
runner.start();
try {
runner.join();
//正常执行完成
finished = true;
} catch (InterruptedException e) {
//如果捕获到异常说明,executeThread被打断了.
//流程继续往下走,及不需要等待runner执行结束了
//e.printStackTrace();
}
}
};
//执行线程启动
executeThread.start();
}
public void shutdown(long mills) {
long currentTime = System.currentTimeMillis();
while (!finished) {
if ((System.currentTimeMillis() - currentTime) >= mills) {
System.out.println("任务超时,需要结束他!");
executeThread.interrupt();
break;
}
}
finished = false;
}
}
测试用例:
public static void main(String[] args) {
ThreadService2 service = new ThreadService2();
long start = System.currentTimeMillis();
service.execute(() -> {
try {
//这边模拟处理任务
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
//等待10m,任务正常结束
//service.shutdown(10000);
//等待四秒,任务被强制结束
service.shutdown(4000);
long end = System.currentTimeMillis();
System.out.println(end - start);
}