线程
线程的五种状态:新建、就绪、运行、阻塞、终止。
一、线程睡眠,两种方式:
1、Thread.sleep(1000);
2、TimeUnit.SECOND.sleep(1);
效果都是睡眠两秒,这两秒进入阻塞状态,然后恢复就绪状态;阻塞状态下无法获得cpu资源;
其他线程中可以通过该线程对象调用interrupt方法打断其睡眠状态,这时sleep方法会抛出InterruptedException,也是进入就绪状态;
sleep的小应用:
在while(true)这种无限循环中,如果加入睡眠方法,可以防止cpu空转,防止cpu资源浪费;
二、yield
yield的作用是将时间片让出去;
调用方式和sleep一样,Thread.yield();
线程内主动调用yield方法后,线程马上进入就绪状态,和其他线程一起竞争cpu资源,所以这时也有可能继续被任务调度器分配cpu资源;
三、线程优先级
线程有1-10,10个优先级等级;
使用方法:t1.setPriority(Thread.MIN_PRIORITY);
优先级会提示任务调度器优先调度该线程,但关键还是由调度器决定是否调用;
一般情况下,cpu比较空闲时优先级几乎没作用,cpu任务多时,优先级越高获得的时间片越多;
四、join方法
等待线程运行结束;
假如先启动了一个t1线程,主线程继续执行,这时候如果想得到t1线程的运行结果,可以t1.join(),也就是主线程等待t1线程执行结束(这里是调用join方法所在的线程,即主线程进入waiting状态),然后才继续下一步;
同理,如果需要等待多个线程的运行结果,那就调用多个线程的join方法,t1.join(),t2.join() ......
t1.join(1000);带参数的join,单位毫秒,意味着等待一定时间后,不管t1有没有结束,主线程都将继续下一步;
join的底层是wait;
五、interrupt打断
正常打断:
public static void main(String[] args) throws Exception{
Thread t1 = new Thread(() -> {
while (true) {
boolean interrupted = Thread.currentThread().isInterrupted();
// 如果是睡眠中的线程被打断,就会抛出interruptedException异常,打断标记返回false
// 所以针对睡眠被打断,可以在捕获异常处理中将打断标记重置为true
if (interrupted) {
System.out.println("被打断了,退出循环");
break;
}
}
});
t1.start();
// 睡眠一秒后打断t1线程
TimeUnit.SECONDS.sleep(1);
t1.interrupt();
}
这种处理方式比较优雅,t1线程被打断后可以继续做一些后续处理,下一次进入循环后就根据打断标记退出循环
注意:isInterrupted方法调用完不会清除打断标记,下次判断依然返回true;
interrupted方法也返回线程是否被打断,但是调用完之后会清除打断标记,下次判断就返回false;
中断线程,本质是给这个线程一个通知信号,会影响线程内部的中断标识位,线程本身并不会因此改变运行的状态(如终止、阻塞等),只是抛出异常之后立即从run方法返回,然后线程消亡,被打断之后的逻辑不会执行。
两阶段终止:
在一个线程t1里,优雅的终止t2,也就是给t2一个料理后事的机会;
错误思路:t2.stop();强制杀死t2线程,如果t2锁住了某个锁,被杀死之前没来得及释放,那么其他线程就永远无法获取该锁,有可能发生死锁;
System.exit(int);让整个程序都停止,这也不适合,因为我们的目标是停止某一个线程;
public class TestTwoPhaseTermination {
public static void main(String[] args) throws Exception{
Test test = new Test();
test.start();
TimeUnit.MILLISECONDS.sleep(3500);
test.interrupt();
}
}
class Test{
private Thread monitor; // 监控线程
public void start() {
monitor = new Thread(() -> {
while (true) {
boolean interrupted = Thread.currentThread().isInterrupted();
if (interrupted) {
System.out.println("料理后事");
break;
}
try {
TimeUnit.SECONDS.sleep(1);
System.out.println("执行监控记录");
} catch (InterruptedException e) {
e.printStackTrace();
// 针对睡眠被打断,重置打断标记
Thread.currentThread().interrupt();
}
}
});
monitor.start();
}
public void interrupt() {
monitor.interrupt();
}
}
六、interrupt打断park状态的线程
假如有一个t1线程,在t1线程内部任务执行过程加入LockSupport.park();线程进入阻塞;
外部线程如果将阻塞线程打断,阻塞线程将继续执行;
但是这时恢复执行的线程就无法再被park暂停了;
所以,如果想要t1可以继续被park的话,就在t1线程任务内加入Thread.interrupted();或者是Thread.currentThread().interrupt();这样t1就自己将打断标记清空了。
七、守护线程
1、默认情况下,java进程需要等待所有线程结束,才会终止运行。
有一种特殊的线程,守护线程,只要其他非守护线程结束了,及时守护线程自己没有执行完,它也会强行结束。
2、用法:
在使用start方法之前,调用t1.setDaemon(true);那么这个线程就成为守护线程了。
3、垃圾回收线程就是守护线程。

浙公网安备 33010602011771号