多线程2——线程状态、Thread类API

一、线程状态

Thread.State是一个枚举类型,里边包含了线程的所有状态。
我们通过代码来看一下线程都有哪些状态:

public class ThreadState {
    public static void main(String[] args) {
        for (Thread.State state : Thread.State.values()) {
            System.out.println(state);
        }
    }
}

运行结果:
在这里插入图片描述
线程状态主要分为:创建、运行、阻塞、等待、超时等待、终止这几种状态。
下图是线程状态的转换图,对于不同方法的调用线程状态会转变。下面将具体了解这些方法。
在这里插入图片描述

二、Thread相关API

1.Thread所有方法

1>静态方法

调用静态方法的代码行处于哪个线程中,就是获取哪个线程的属性。

  • static int activeCount ():返回当前线程的thread group及其子组中活动线程数的估计(了解);
  • static Thread currentThread() :返回对当前正在执行的线程对象的引用。这个方法处于哪个线程就获取这个线程的引用;
  • static boolean interrupted()判断当前线程是否中断如果为true(当前线程为中断状态),就清除中断标志
  • static void sleep(long mi1lis)使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。时间单位是毫秒;
  • static void yield()线程让步,作用------->将当前线程由运行态转变为就绪态;

Thread.xxx:都是静态方法,作用于当前线程。但wait()方法是实例方法,也作用于当前线程

2>实例方法
  • long getId():返回此线程的标识符。(了解)
  • String getName ()返回此线程的名称
  • int getPriority():优先级更高的线程,更有可能先执行,但不是一定,只返回此线程的优先级。是几率更大 (了解)
  • Thread.State getState()返回此线程的状态。
  • void interrupt()中断这个线程。 仅仅是将线程的中断标志位设为true。
  • boolean isAlive()测试这个线程是否活着。(了解)
  • boolean isDaemon()测试这个线程是否是守护线程。(了解)
  • boolean isInterrupted()测试这个线程是否被中断, 不会清除中断标志
  • void join()等待这个线程死亡
  • void join(long millis):等待这个线程死亡最多mill1s毫秒。
  • void run ( ):在线程内如果我们自己定义了run方法,启动线程就会执行我们自己定义的run方法,否则就执行线程自身的。
  • void setDaemon (boolean on)将此线程标记为daemon线程或用户线程。(了解)
  • void setName (String name )将此线程的名称更改为等于参数name 。(了解)
  • void setPriority (int newPriority)更改此线程的优先级。(了解),设置的优先级越高,线程就有可能先执行,不是一定先执行
  • void start()使该线程开始执行; Java虚拟机调用此线程的run方法。

2.线程启动:start()

注意:
我们要知道start()实例方法是启动该线程,此时线程由创建态变为就绪态,当线程为就绪态时就可以等待操作系统进行调度,调度到之后就执行线程的run方法(线程的任务)。

下面我们先来看一段代码:

public class ThreadFirst {
    public static void main(String[] args)throws InterruptedException  {
    //新创建一个线程
   Thread newthread=  new Thread(new Runnable() {
        @Override
        public void run()
        {
            try {
                Thread.sleep(99999999999L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    newthread.start();
    }
}

我们运行该代码,并且在cmd中输入jconsole连接到Java监视窗口。如下图所示:
在这里插入图片描述
重点:

  1. Thread newthread= new Thread(new Runnable()…); 仅仅是创建了一个线程对象,此时相当于创建了上图的Thread-0线程,并且该线程的状态为创建态
  2. newthread.start(); 执行这行代码时,Thread-0线程由创建态转变为就绪态,此时就可以等待操作系统调度,调度到了就转变为运行态(执行线程中的run方法)。
  3. 线程之间是可以并发也可以并行的(具体看操作系统)。见下图:
    在这里插入图片描述

3.线程让步: Thread.yield()

线程让步就是让该线程由运行态转变为就绪态。一般用于让其他线程完成自己的任务后,再执行当前线程的工作。
先来看下面代码:

public class ThreadYield {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        }).start();

        //等待new Thread所有线程执行完毕,才会执行别的操作(main线程打印main线程的名字)
        while (Thread.activeCount()>1)//当前活跃的线程数大于1时就执行循环里边的代码(让main线程由运行态转变为就绪态),直到new Thread线程执行完毕,所以先打印thread-0,后打印main
        {
            //将当前线程由运行态转变为就绪态
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName());

    }
}

Thread.activeCount()>1:当前活跃的线程数大于1时就执行循环里边的代码(让main线程由运行态转变为就绪态)

那么这段代码运行结果会先打印main还是Thread-0线程呢?精华就在while循环这块了。
先来看一下运行结果,再进行一步一步分析:
在这里插入图片描述
从结果可以看出,是先打印的Thread-0线程。分析:

  • 当Thread-0线程通过start方法启动起来之后,当前线程是2个(一个是Thread-0线程,另一个就是Java的main线程)
  • 当代码走到while循环时,先判断当前线程的数量是否大于1,显然2>1,然后就执行while循环体( Thread.yield():当前线程让步)
  • 对于Thread.yield()这行代码来说当前线程为Java main线程,所以此时main线程让步,一直等待Thread-0线程执行完run方法(打印Thread-0),直到Thread-0线程退出,活跃线程数就为1,再打印main线程名。

简单来说,线程让步,就是将正在运行的线程停止手中的工作,让其他线程先行,其他线程做完自己的活之后再做自己的工作。

4.守护线程:.setDaemon(true)

  • 用户线程: 我们平常创建的普通线程。
  • 守护线程: 用来服务于用户线程;不需要上层逻辑介入。

我们先通过一段代码观察一下效果:

public class DaemonThread {
    public static void main(String[] args) {
        Thread t=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(999999999L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //设置守护线程
        t.setDaemon(true);
        t.start();
    }
}

设置守护线程:
加粗样式
没有设置守护线程:
在这里插入图片描述
通过结果发现:没有设置守护线程的一直处于没有退出。
因此:
当线程只剩下守护线程的时候,JVM就会退出;如果还有其他的任意一个用户线程还在,JVM就不会退出。

注意:
thread.setDaemon(true):必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。

5.线程的等待:join()/activeCount()+yield()

1)join()

join():让当前线程进行阻塞(运行态–》阻塞态)等待(需要一定的条件);比如线程B(当前线程)中调用了线程A的Join()方法,直到线程A执行完毕或者等待线程A执行指定的时间后,才会继续执行线程B。

  • 有参方法( t.join(1000) ):等待 t 线程1000毫秒 ,1000毫秒后当前线程代码向下执行。
  • 无参方法( t.join() ) :等待线程 t 执行完毕,当前线程再向下执行代码。

代码:

public class ThreadJoin {
    public static void main(String[] args) {
      Thread t=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                    System.out.println(Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();

        //当前线程: t.join();调用这行代码的时候时是在哪个线程,这个线程就是当前线程
        //t是new Thread这个线程的引用
         //让t这个线程先执行完,再执行main线程
        try { 
            t.join(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
   System.out.println(Thread.currentThread().getName());
  }
}

上述代码就是让main线程阻塞2000毫秒(让t线程先执行2000毫秒)之后再执行main线程的代码。

2)activeCount()结合yield()

上面说线程让步已经使用过一次。

  • Thread.activeCount()>1:当前活跃的线程数大于1时就执行循环里边的代码
  • Thread.yield(): 将当前线程由运行态转变为就绪态(让main线程由运行态转变为就绪态)

在这里插入图片描述
相当于等待Thread-0线程执行完,再执行main线程。

6.中断这个线程:interrupt()

interrupt():表示中断该线程,它并没有让正在运行线程停止了,只是将该线程的中断标志位设置为true。

中断标志位:

  1. 线程启动以后:中断标志位=false

  2. 在线程运行态中,处理线程中断,需要自行通过判断中断标志位,来进行中断的处理逻辑。判断标志位方法

    • Thread.interrupted(): 返回当前线程的中断标志位,并清除中断标志(恢复为false)
    • Thread.currentThread().isInterrupted(): 返回指定线程的中断标志位,不清除中断标志
  3. 通过 thread 对象调用 interrupt() 方法通知该线程停止运行,thread 收到通知的方式有两种:

    • 如果线程调用了 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通知,并重置中断标志=true。
    • 否则,其他情况只是内部的一个中断标志被设置为true,thread 可以继续运行。
 public static void test4()
    {
        Thread t=new Thread(new Runnable() {
            @Override
            //如果线程调用了 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通知,清除
            //中断标志
            public void run() {
                for (int i = 0; i < 10; i++)
                {
                    //调用静态方法:返回中断标志位,返回后还要重置中断标志位(只有标志位是true,才会重置为false)
                    System.out.println(Thread.interrupted());//一个true,九个false                                                
                  //不会重置中断标志位
                    System.out.println(Thread.currentThread().isInterrupted());//十个false 
                }//一个true,九个false
            }
        });
        //启动子线程
        t.start();//t线程的中断标志位=false

        //在main线程中中断子线程
        t.interrupt();//t线程的中断标志位=true
    }
    public static void main(String[] args) {
        //test1();
        //test2();
        test4();//抛出异常
    }

运行结果:
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值