上次我们了解了多线程的五种创建方法,今天来学习Thread的基本用法。
目录
run和start
Thread常见的构造方法
Thread的几个常见属性
后台线程
是否存活
线程终止
1.使用标志位
2.使用Thread自带的标志
等待线程
run和start
首先需要理解Thread的run和start的区别:
run描述了线程要做的工作,start让内核创建一个PCB,也就是让操作系统新建一个线程。
如果不调用start,那么就不会在内核中有新的线程。
Thread常见的构造方法
第二、三、四个方法,其实都只是给线程起了个名字,线程默认的名字,叫做thread-0之类的,我们新建一个线程来看一下。
public class demo6 {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while(true){
System.out.println("hello");
}
}
},"mythread_demo");
t.start();
}
}
通过jconsole可以看到我们自己命名的线程出现了。
Thread的几个常见属性
1.ID 是线程的唯一标识,不同线程不会重复
2.名称是各种调试工具用到,这里是构造方法里取的名字
3.线程状态
4.优先级高的线程理论上来说更容易被调度到
5.关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
6.是否存活,即简单的理解,为run方法是否运行结束了
7.线程的中断问题,我们待会再说。
后台线程
后台进程,又叫做守护线程。
前台线程,会阻止进程结束.前台线程的工作没做完,进程是完不了的。
后台线程,不会阻止进程结束.后台线程工作没做完,进程也是可以结束的。JVM必须在所有前台进程结束后,才会结束运行。
代码里手动创建的线程,默认都是前台的.包括main 默认也是前台的.
其他的jvm自带的线程都是后台的.
也可以手动的使用setDaemon设置成后台线程.是后台线程,就是守护线程。
t.setDaemon(true);
可以用这个代码把t设置成后台进程,这样进程的结束就和t无关。
是否存活
isAlive是在判断,当前系统里面的这个线程是不是真的有了。
在真正调用start之前,调用t.isAlive就是false。调用start之后, isAlive就是true。
线程终止
终止的意思是,不是让线程立即就停止,而是通知线程,你应该要停止了。是否真的停止取决于线程这里具体的代码写法。
1.使用标志位
在创建多线程的代码前面,加上一个flag变量,作为标志位,最后由flag控制进程是否停止。这样在最后停止时,就是由flag决定的。
但是如果在while(flag)中sleep休眠时,自变量这种方式不能及时响应
但是 sleep 在唤醒的时候,还会做一件事,把刚才设置的这个标志位再设置回 false(清空了标志位)
这就导致,当 sleep 的异常被 catch 完了之后, 循环还要继续执行!!!
那么就是说,此时的flag并没有起作用。
因此,这里只是告诉让这个线程结束.这个线程是否要结束,啥时候结束都是线程内部自己代码来决定的。
2.使用Thread自带的标志
while(!Thread.currentThread().isInterrupted()){
t.interrupte就是终止t线程,main 线程调用t.interrupt()相当于main通知 t 你要终止了。
其实跟前面的flag差不过,只是把一个boolean操作封装到Thread的方法里面了。
interrupt会做两件事:
1.把线程内部的标志位(boolean)给设置成true。
2.如果线程在进行sleep,就会触发异常,把sleep唤醒。
但是sleep在唤醒的时候,还会做一件事,把刚才设置的这个标志位再设置回false.(清空了标志位)
这就导致,当sleep的异常被catch完了之后,循环还要继续执行。
那岂不是加不加这个interru都没用?反正遇到线程sleep的时候总是会继续运行?
此时我们可以在循环最后加一个break
这样子线程t就会立刻响应你的终止请求。
等待线程
线程是一个随机调度的过程,等待线程,做的事情,就是在控制两个线程的结束顺序。
通过join来实现