栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java——线程五大状态

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java——线程五大状态

文章目录
  • 线程状态(简图)
  • 线程状态(详细)
  • 线程方法
  • 线程停止
  • 线程礼让 yield
  • 线程插队 join
  • 线程状态监测
  • 线程优先级
  • 守护线程 daemon

线程状态(简图)

线程从诞生到使用,再到最后的死亡,大体上符合下列的几种状态转换。

就像一个人的生老病死。

【注意:】

  • 1、new Thread() 只是让一个线程诞生,并不会让其运行,此时只是一个简单的Java类。

  • 2、调用 t.start(),只会让线程进入就绪状态,并不会立即执行该线程。

    原因在于:需要等待CPU的调度。(CPU的切片)

  • 3、阻塞状态解除后,并不会立即执行该线程,此时依旧需要等待CPU对其进行调度。

线程状态(详细)

线程状态的切换,大致为以上的逻辑图,如果深究其各种Java的API调用,则可以参考下列逻辑图。

【注意:】

  • 1、等待
    处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。
  • 2、超时等待
    处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。

其实等待和超时等待都属于阻塞。

该图逻辑参考:Java线程的6种状态及切换(透彻讲解)

线程方法
方法说明
setPriority(int newPriority)设置线程的优先级
static native void sleep(long millis)在指定的毫秒数内让当前正在执行的线程休眠
void join()等待该线程终止
static void yield()暂停当前正在执行的线程对象,并执行其他线程。
void interrupt()中断线程。不建议使用
boolean isAlive()测试线程是否处于活动状态。
线程停止

在java.lang.Thread类中,存在类似stop()、destroy()等让线程消亡的方法。但不推荐使用。

【疑问:】如何停止一个线程?

1、建议线程正常停止。使用次数、不建议死循环(使用死循环也需要增加延时,避免CPU卡死)
2、建议使用标志位,让线程自己停止下来。
3、不要使用stop()、destroy()等过时或者JDK不建议的方法。

接下来编写一个案例,来掩饰如何正确的停止线程:

package thread;

import java.util.concurrent.TimeUnit;

public class StopThread implements Runnable {

    // 1、设置标识位
    boolean flag = true;

    @Override
    public void run() {
        int i = 0;
        while (flag){
            System.out.println("run ... thread "+ i++ +" flag="+flag);
        }
    }

    // 2、设置公开方法 停止线程
    public void stop(){
        flag = false;
    }

    public static void main(String[] args) throws InterruptedException {
        StopThread stopThread = new StopThread();
        new Thread(stopThread).start();

        // 保证线程能够被CPU调度执行
        TimeUnit.SECONDS.sleep(5);

        for (int i = 0; i < 1000; i++) {
            //System.out.println("main i="+i);
            if(i == 900){
                // 调用stop 停止线程
                stopThread.stop();
                System.out.println("start stop thread");
                //break;
            }
        }

    }
}

其运行效果如下所示:

  • 结束线程之前:

  • 结束线程后:

线程礼让 yield

Thread类中提供了一种礼让方法,使用yield()方法表示,它只是给当前正处于运行状态下的线程一个提醒,告知它可以将资源礼让给其他线程,但这仅仅是一种暗示,没有任何一种机制保证当前线程会将资源礼让。

yield()方法使具有同样优先级的线程有进入可执行状态的机会,当当前线程放弃执行权时会再度回到就绪状态。

对于支持多任务的操作系统来说,不需要调用yeild()方法,因为操作系统会为线程自动分配CPU时间片来执行。

  • 礼让线程,让当前正在执行的线程暂停,但不阻塞
  • 线程进行礼让操作后,会由运行状态转化为就绪状态
  • 礼让只是让CPU重新进行调度,礼让不一定成功!

接下来,根据代码来演示线程礼让到底是什么样子的:

package thread;

public class YieldThread {
    public static void main(String[] args) {
        MyYiledThread myYiledThread = new MyYiledThread();

        new Thread(myYiledThread,"A").start();
        new Thread(myYiledThread,"B").start();
    }
}
class MyYiledThread implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程执行");
        // 通过注释掉下列代码,分别执行礼让的现象
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"线程结束");
    }
}

现象:

  • 注释掉 Thread.yield(); 后:

  • 不注释 Thread.yield(); 后:

线程插队 join

当前线程里调用其它线程1的join(),当前线程阻塞,但不释放对象锁,直到线程1执行完毕或者millis时间到,当前线程进入可运行状态。

package thread;

public class ThreadJoin implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(" 线程插队...."+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {

        // 创建线程并执行
        ThreadJoin threadJoin = new ThreadJoin();
        Thread thread = new Thread(threadJoin);
        thread.start();

        for (int i = 0; i < 500; i++) {
            if(i == 200){
            	// 主线程中,对其他线程进行join()调用
            	// 让其他线程可以插队
                thread.join();
            }
            System.out.println("main = "+i);
        }
    }
}

其运行后的状态如下所示:

【疑问:】为什么 插队线程 不是从0全部插入?

因为没有对其他线程进行设定运行时间,当主线程的i还未到200时,此时其他线程和主线程是并行状态。

当主线程中执行了其他线程的join(),也就是相当于主线程让其他线程插队,此时主线程阻塞,让其他线程将剩下的完全执行完成后,主线程才能继续执行!

【扩展:】
假设有一个需求:

需要异步执行一段代码,然后再主线程中获取最终执行的结果。

就像下列代码所示:

package thread;

import java.util.concurrent.TimeUnit;

public class ThreadJoin2 implements Runnable {

    private int a = 0;

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            a ++;
        }
    }

    public int getA() {
        return a;
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadJoin2 threadJoin2 = new ThreadJoin2();
        Thread thread = new Thread(threadJoin2);
        thread.start();
        
        System.out.println(threadJoin2.getA());
		
		// 一般情况下,在时间可控时,采取sleep 对主线程延迟,然后获取最终结果
        // TimeUnit.SECONDS.sleep(5);
        // System.out.println(threadJoin2.getA());
		
		// 如果线程执行的时间不可控,则需要让主线程调用其他线程的 join()
		// 保证主线程阻塞,并让其他线程先执行,直到其他线程执行完毕
        thread.join();
        System.out.println(threadJoin2.getA());
    }
}

线程状态监测

上面已经说到了线程的各个状态变化逻辑信息,以及主要的几个方法讲解,接下来我们来探究一个线程的生命周期是怎么样变化的。

在JDK 1.8的中文文档中,关于线程状态归纳于java.lang.Thread.State这个枚举类中。

下面,以代码的方式说明线程在其整个生命周期内状态的变更现象:

package thread;

import java.util.concurrent.TimeUnit;

public class ThreadState {
    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(() -> {
            // 让线程执行一定的次数
            for (int i = 0; i < 5; i++) {
                // 每次延迟1s
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 循环结束后输出
            System.out.println("循环结束....");
        });

        // 此时的线程未进行start
        Thread.State state = thread.getState();

        // 输出状态
        System.out.println(state);

        // 启动线程
        thread.start();
        state = thread.getState();
        System.out.println(state);

        // 启动之后,因为run()中是不断的循环,此处写一个循环不断打印其状态信息
        // 满足的条件就是:当前线程未消亡
        while (state != Thread.State.TERMINATED){
            // 延迟打印
            TimeUnit.MILLISECONDS.sleep(100);
            // 重新赋值
            state = thread.getState();
            System.out.println(state);
        }
    }
}

执行上述代码后,控制台输出日志信息如下所示:

NEW
RUNNABLE
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
......
TIMED_WAITING
循环结束....
TERMINATED
线程优先级

Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。

线程调度器按照优先级来决定应该调度那个线程来执行。

线程的优先级用整型数字表示,范围为1~10。

【注意:】

优先级高的线程不一定优先执行。
线程的执行,依旧是通过CPU的调度。

只能说优先级越高,线程被调度的权重(几率)越大。


Java中,关于线程优先级的设置和获取为以下方式:

接下来采取代码案例的方式,说明优先级的设置和获取,代码如下所示:

package thread;

// 线程优先级
public class ThreadPriority {
    public static void main(String[] args) {
        // 主线程默认的优先级
        System.out.println(Thread.currentThread().getName()+" --- "+Thread.currentThread().getPriority());

        // 获取线程的执行结构体
        MyThread myThread = new MyThread();

        // 创建多个线程
        Thread t1 = new Thread(myThread,"t1");
        Thread t2 = new Thread(myThread,"t2");
        Thread t3 = new Thread(myThread,"t3");
        Thread t4 = new Thread(myThread,"t4");
        Thread t5 = new Thread(myThread,"t5");
        Thread t6 = new Thread(myThread,"t6");

        // 必须先设置优先级再启动才行;否则设置优先级无效
        t2.setPriority(1);
        t3.setPriority(6);
        t4.setPriority(3);
        t5.setPriority(Thread.MAX_PRIORITY); // 最大优先级 10
        t6.setPriority(8);

        t1.start(); // t1 采取默认优先级
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
    }
}
class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+" --- "+Thread.currentThread().getPriority());
    }
}

优先级越高,不代表一定优先执行!
线程优先级低,只是意味着获取调度的概率越低,并不是优先级低就不会被调用了。

这里就需要看CPU的调度了。

性能倒置

守护线程 daemon

关于线程的划分一般分为如下:

  • 线程分为用户线程和守护线程。
  • 虚拟机必须确保用户线程执行完毕。----- main 线程
  • 虚拟机不用等待守护线程执行完毕。 ----- gc

测试代码如下所示:

package thread;

import java.util.concurrent.TimeUnit;

// 守护进程测试
public class DaemonTest {
    public static void main(String[] args) {
        MyDaemo myDaemo = new MyDaemo();
        MyThread2 myThread2 = new MyThread2();

        // 设置守护线程
        Thread mdT = new Thread(myDaemo);
        mdT.setDaemon(true); // 默认为false,表示用户线程;当设置为false后表示守护线程
        mdT.start(); // 启动线程

        new Thread(myThread2).start(); // 默认为用户线程

    }
}

// 守护线程
class MyDaemo implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println("守护线程执行中....");
            try {
                TimeUnit.MILLISECONDS.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

// 测试线程
class MyThread2 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("测试线程执行中.... "+i);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


【疑问:】守护线程为什么会终止?

虚拟机不用等待守护线程执行完毕。

当用户线程执行完毕之后,程序也就结束了,此时jvm 虚拟机不会立即关闭,在关闭的过程中,守护线程是还在继续执行的。
当虚拟机完全关闭之后,守护线程也就结束运行了。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/314467.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号