线程

线程的五种状态:新建、就绪、运行、阻塞、终止。

一、线程睡眠,两种方式:

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、垃圾回收线程就是守护线程。

 

posted @ 2021-04-19 23:19  DustAsh  阅读(107)  评论(0)    收藏  举报