目录
2.8 interrupted() 和isInterrupted()
2.14 setPriority(int newPriority)
一、概要
目前CPU的运算速度已经达到了百亿次每秒,甚至更高的量级,家用电脑即使维持操作系统正常运行的进程也会有数十个,线程更是数以百计。线程是CPU的调度和分派的基本单位,为了更充分地利用CPU资源以及提高生产率和高效地完成任务,在现实场景中一般都会采用多线程处理。
1.1 线程的生命周期
线程的生命周期大致可以分为下面五种状态:New(新建状态)、RUNABLE(就绪状态)、RUNNING(运行状态)、休眠状态、DEAD(终止状态)
1、新建状态,是线程被创建且未启动的状态;这里的创建,仅仅是在JAVA的这种编程语言层面被创建,而在操作系统层面,真正的线程还没有被创建。
Thread t1 = new Thread()
2、就绪状态,指的是调用start()方法之后,线程等待分配给CPU执行(这时候,线程已经在操作系统层面被创建)
t1.start()
3、运行状态,当CPU空闲时,线程被分得CPU时间片,执行Run()方法的状态
4、阻塞状态,运行状态的线程,如果调用一个阻塞的API或者等待某个事件,那么线程的状态就会转换到休眠状态,一般有以下几种情况
- 同步阻塞:锁被其它线程占用
- 主动阻塞:调用Thread的某些方法,主动让出CPU执行权,比如:sleep()、join()等方法
- 等待阻塞:执行了wait()方法
5、终止状态,线程执行完(run()方法执行结束
)或者出现异常就会进入终止状态,有三个原因会导致线程死亡:
① run方法正常退出而自然死亡;
② 一个未捕获的异常终止了run方法而使线程猝死;
③ 线程调用 stop()方法、destory()方法或 run()方法
为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法,如果是可运行或被阻塞,这个方法返回true;如果线程仍旧是new状态且不是可运行的,或者线程死亡了,则返回false。
用一个图片来作为总结:
对应的是JAVA中Thread类State
中的六种状态
public class Thread implements Runnable {
//.........
public enum State {
NEW, // 初始化状态
RUNNABLE, // 可运行/运行状态
BLOCKED, // 阻塞状态
WAITING, // 无时限等待
TIMED_WAITING, // 有时限等待
TERMINATED; // 终止状态
}
// ..........
}
休眠状态(BLOCKED、WAITING、TIMED_WAITING)与RUNNING状态的转换
二、一些常用方法
2.1 currentThread()
返回对当前正在执行的线程对象的引用。
2.2 getId()
返回此线程的标识符
2.3 getName()
返回此线程的名称
2.4 getPriority()
返回此线程的优先级,默认是5
2.5 isAlive()
测试这个线程是否还处于活动状态。
什么是活动状态呢?
活动状态就是线程已经启动且尚未终止。线程处于正在运行或准备运行的状态。
2.6 sleep(long millis)
使当前正在执行的线程以指定的毫秒数“休眠”(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。
2.7 interrupt()
中断这个线程。不要以为它是中断某个线程!它只是线线程发送一个中断信号,让线程在无限等待时(如死锁时)能抛出异常,从而结束线程,但是如果你吃掉了这个异常,那么这个线程还是不会中断的!
2.8 interrupted() 和isInterrupted()
interrupted():测试当前线程是否已经是中断状态,执行后具有将状态标志清除为false的功能
isInterrupted(): 测试线程Thread对相关是否已经是中断状态,但部清楚状态标志
public class ThreadDemo01 extends Thread{
@Override
public void run() {
for (int i = 0; i < 5000; i++) {
System.out.println(Thread.currentThread().getName()+"正在运行...,目前是第"+i+"次");
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程开始...");
ThreadDemo01 threadDemo01 = new ThreadDemo01();
threadDemo01.setName("子线程一");
threadDemo01.start();
Thread.sleep(10);
threadDemo01.interrupt();//终端线程
System.out.println("主线程结束...");
}
}
运行结果:
运行上诉代码你会发现,线程并不会终止。
针对上面代码的一个改进:
interrupted()方法判断线程是否停止,如果是停止状态则return
public class ThreadDemo01 extends Thread{
@Override
public void run() {
for (int i = 0; i < 5000; i++) {
if(this.isInterrupted()){//判断当前线程是否已经是中断状态
System.out.println(Thread.currentThread().getName()+"已经是中断状态了,我要退出了...");
return;
}
System.out.println(Thread.currentThread().getName()+"正在运行...,目前是第"+i+"次");
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程开始...");
ThreadDemo01 threadDemo01 = new ThreadDemo01();
threadDemo01.setName("子线程一");
threadDemo01.start();
Thread.sleep(10);
threadDemo01.interrupt();//终端线程
System.out.println("主线程结束...");
}
}
运行结果:
通过运行上面的代码,我们可以看出,interrupt()方法只能终止线程的状态,我们需要借助interrupted()方法判断线程是否停止,然后通过return来进行停止线程。
2.9 setName(String name)
将此线程的名称更改为等于参数 name 。
2.10 isDaemon()
测试这个线程是否是守护线程。
2.11 setDaemon(boolean on)
将此线程标记为 daemon线程或用户线程。
Java线程有两类:
用户线程:运行在前台,执行具体的任务,程序的主线程,连接网络的子线程等都是用户线程
守护线程:运行在后台,为其他前台线程服务
特点:一旦所有用户线程都结束运行,守护线程会随JVM一起结束工作
应用:数据库连接池中的监测线程JVM启动后的监测线程
最常见的守护线程:垃圾回收线程
如何设置守护线程:可以通过Thread类的setDaemon(true)方法来设置当前的线程为守护线程
1. setDaemon(true)必须在start()方法前执行,否则会抛出IllegalThreadStateException异常
2. 在守护线程中产生的新线程也是守护线程
3. 不是所有的任务都可以分配给守护线程来执行,比如读写操作或者计算逻辑
案例一:子线程一和子线程二都是守护线程,我们看JVM结束,两个子线程是否会结束?
public class ThreadDemo01 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在运行...,目前是第"+i+"次");
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程开始...");
ThreadDemo01 threadDemo01 = new ThreadDemo01();
threadDemo01.setName("子线程一");
threadDemo01.setDaemon(true);//将子线程一设置为守护线程
threadDemo01.start();
ThreadDemo01 threadDemo02 = new ThreadDemo01();
threadDemo02.setName("子线程二");
threadDemo02.setDaemon(true);//将子线程一设置为守护线程
threadDemo02.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程结束...");
}
}
运行结果:
我们发现JVM结束,两个子线程也随之结束。
案例二:子线程一为守护线程,子线程二为非守护线程
public class ThreadDemo01 extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在运行...,目前是第"+i+"次");
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程开始...");
ThreadDemo01 threadDemo01 = new ThreadDemo01();
threadDemo01.setName("子线程一");
threadDemo01.setDaemon(true);//将子线程一设置为守护线程
threadDemo01.start();
ThreadDemo01 threadDemo02 = new ThreadDemo01();
threadDemo02.setName("子线程二");
threadDemo02.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程结束...");
}
}
运行结果:
注意:为什么主线程结束,守护线程还在继续执行呢?因为主线程虽然结束了,但是线程二还在继续执行,我们的JVM还没有结束,所以子线程一还在继续执行
2.12 join()
在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是 主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。
join()的作用是:“等待该线程终止”,这里需要理解的就是该线程是指的主线程等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行
不加join
public class ThreadDemo01 extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程"+Thread.currentThread().getName()+"正在运行...");
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程开始...");
ThreadDemo01 threadDemo01 = new ThreadDemo01();
threadDemo01.setName("子线程");
threadDemo01.start();
System.out.println("主线程结束...");
}
}
运行结果:
加join
public class ThreadDemo01 extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程"+Thread.currentThread().getName()+"正在运行...");
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程开始...");
ThreadDemo01 threadDemo01 = new ThreadDemo01();
threadDemo01.setName("子线程");
threadDemo01.start();
threadDemo01.join();
System.out.println("主线程结束...");
}
}
运行结果:
注意:我们可以发现主线程一定会等子线程运行结束了才结束;join()一定要加在线程启动之后,才有效。
2.13 yield()
yield()方法的作用是放弃当前的CPU资源,将它让给其他的任务去占用CPU时间。
Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程。
yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。
public class ThreadDemo01 extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"正在运行...,目前是第"+i+"次");
//当i为5时,该线程就会把CPU时间让掉,让其他或者自己的线程执行(也就是谁先抢到谁执行)
if(i==5){
Thread.yield();
}
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程开始...");
ThreadDemo01 threadDemo01 = new ThreadDemo01();
threadDemo01.setName("子线程一");
threadDemo01.start();
ThreadDemo01 threadDemo02 = new ThreadDemo01();
threadDemo02.setName("子线程二");
threadDemo02.start();
System.out.println("主线程结束...");
}
}
运行结果:
从上面的运行结果可以看出:线程一在运行到第6次的时候(即i=5),将CPU的占有权释放掉,这时候OS会重新挑选线程执行;当线程二在运行到第6次的时候,将CPU的占有权释放掉,这时候OS也会重新挑选线程执行,但是目前系统只有线程二供挑选,所以线程二继续执行。
sleep()和yield()的区别
sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会被执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
sleep 方法使当前运行中的线程睡眼一段时间,进入不可运行状态,这段时间的长短是由程序设定的,yield 方法使当前线程让出 CPU 占有权,但让出的时间是不可设定的。
实际上,yield()方法对应了如下操作:先检测当前是否有相同优先级的线程处于同可运行状态,如有,则把 CPU 的占有权交给此线程,否则,继续运行原来的线程。所以yield()方法称为“退让”,它把运行机会让给了同等优先级的其他线程 另外,sleep 方法允许较低优先级的线程获得运行机会,但 yield() 方法执行时,当前线程仍处在可运行状态,所以,不可能让出较低优先级的线程些时获得 CPU 占有权。在一个运行系统中,如果较高优先级的线程没有调用 sleep 方法,又没有受到 I\O 阻塞,那么,较低优先级线程只能等待所有较高优先级的线程运行结束,才有机会运行。
2.14 setPriority(int newPriority)
更改此线程的优先级,注意:优先级高不代表一定会提前运行该线程,只是代表该线程被挑选的概率比较高
- MIN_PRIORITY = 1
- NORM_PRIORITY = 5
- MAX_PRIORITY = 10
用法:
public class ThreadDemo01 extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"正在运行...,目前是第"+i+"次");
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程开始...");
ThreadDemo01 threadDemo01 = new ThreadDemo01();
threadDemo01.setName("子线程一");
threadDemo01.setPriority(MIN_PRIORITY);
threadDemo01.start();
ThreadDemo01 threadDemo02 = new ThreadDemo01();
threadDemo02.setName("子线程二");
threadDemo01.setPriority(MAX_PRIORITY);
threadDemo02.start();
System.out.println("主线程结束...");
}
}
运行结果: