JAVA多线程(二)

在之前了解了JAVA多线程的两种创建与启动方式之后,今天要说的是结束线程,以及对线程的控制,话不多说,先看看结束线程。
假如有这样的一个线程类:
在这里插入图片描述
不难看出如果该线程一旦启动,就会无限循环,而这时我们想通过另一个线程让其停止,比如主线程:
在这里插入图片描述
这里我在主线程中写了一个循环语句,当i的值等于100时,调用stop()方法终止线程,但很明显,stop()方法上划上了一条横线,说明该方法不再推荐使用,因为其存在不安全性。我们可以这样来写:
通常来说,线程执行体中的功能是持续性的,我们会将一些持续执行的语句放在线程执行体中,比如上面的线程类中我们循环打印的语句块,那么换句话讲,循环结束,也就意味着线程的结束,只要控制住循环,也就控制住了线程,这种方式也被称为通知方式,究竟如何做,我们来看一看。

既然问题变成了控制循环,我们可以先声明一个标记:

 public boolean flag=true;

循环依旧是死循环:

@Override
    public void run() {
        while (flag){

            System.out.println(Thread.currentThread().getName()+":"+i++);
        }
    }

再编写一个方法,用于改变flag的值:

  public void setFlag(){
        flag=false;
    }

总的来说:
在这里插入图片描述
那么我们在test类中,这样写即可:
在这里插入图片描述
为了让效果明显,我将主线程中的循环语句打印了一个比较大的数,当i等于我们指定的一个数值时,调用之前写好的setFlag方法,将子线程中的flag改为false,从而退出循环完成线程的结束。

接下来我们说一说线程的控制,由于JAVA的多线程是抢占式,即高优先级的线程抢占CPU资源,在这里我们先说一说Thread类中的几个方法:

第一个是Sleep方法
sleep(long millis):是一个静态方法,使当前执行线程进入睡眠状态
同样我们使用上面类似的例子,首先是线程类:

 try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            //*e.printStackTrace();通常不做处理,用于唤醒睡眠状态的线程*//*
        }

        while (i<=100){
        
            System.out.println(Thread.currentThread().getName()+":"+i++);
        }
    }

使用sleep方法需要try-catch处理异常,而一般我们不对异常进行处理,因为该异常会唤醒睡眠状态的线程,往后我们会看到效果。由于毫秒时间比较短暂,设置长一点时间比较容易看到效果,这里我们就设置5秒钟的睡眠。

接下来是Test类:
在这里插入图片描述
我们会发现点击运行后,过了5秒钟后控制台才开始打印输出线程体中的功能。

如果我们将sleep方法写入循环体:
在这里插入图片描述
则运行效果就是每隔一秒钟打印一次数字。

既然有睡眠,那么就有唤醒方法:

interrupt():中断阻塞(睡眠)状态的线程

比如在上面的例子中,我们将线程睡眠5秒后再执行,调用interrupt()方法即可唤醒:

 t2.interrupt();

而该方法是通过抛出异常来唤醒睡眠状态的线程,也就是上面我们没有进行处理的异常,我们将其注释打开再运行:

 try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

在这里插入图片描述
运行结果:
在这里插入图片描述
JVM会以抛出一个异常的方式,告诉我们该睡眠的线程已经被唤醒,因此我们一般不对该异常做处理。

而如果我们将sleep()方法放入循环语句中:
在这里插入图片描述
在这里插入图片描述
再执行:
在这里插入图片描述
发现线程在一次被唤醒后,又进入了睡眠状态,这表示interrupt()方法是用于判断当前线程是否处于睡眠状态,当线程睡眠时,唤醒,而由于该线程是一个循环的,而interrupt()方法只调用了一次,所以对后面的睡眠状态不再唤醒,那么假如我们想要实现只要线程睡眠就进行唤醒,应该怎么写呢?

一种方法是,我们只要能循环去调用唤醒方法即可,那么我们可以写下一个死循环:
在这里插入图片描述
但这样的方式也会有问题,因为该循环语句会一直执行,即使线程已经处于唤醒状态了,该线程永远不会停下来,因此我们有了另一个方法:

isAlive():判断当前线程是否处于存活状态

我们将该方法放入while循环即可:
在这里插入图片描述
这样只有当线程处于存活状态时,该循环语句才会一直执行。

类似于sleep()方法,还有一个方法与其功能相同,但用法不同:

join() /join(long millis):是一个实例方法,使当前执行线程进入阻塞状态

两者不同在于一个是实例方法一个是静态方法,我们看看这个方法怎么用,同样还是上面的例子:
在这里插入图片描述
我们在主线程中加上一个打印100至200的循环语句,并将join方法加入,同样不处理异常:
在这里插入图片描述
反复运行后我们会发现,执行的顺序如下:
在这里插入图片描述
主线程会在子线程结束后才开始运行,也就是说明主线程被阻塞了,那么再回到join方法:

t2.join();

我们可以简单的理解为,子线程t2向正在运行的线程,也就是主线程申请了调用CPU的使用权,当t2执行完后,主线程再进行执行,由于我们并没有在括号内填入毫秒数,所以t2会执行完才让主线程执行,那如果我们在括号内填入毫秒数,那么就意味着t2会在我们指定的时间内先调用CPU,时间到了之后主线程就会恢复执行。

最后,我们再说一个不是很常用的方法:

yield():线程让步

简单来说,由于JAVA的多线程是抢占式,线程会互相抢占任务,而yield方法则是用于暂缓当前线程,让其他的线程更有机会执行,由于效果不是很明显,就不演示了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值