多线程基础(4)-线程的状态和转化的方法

 

4.1 操作系统中的线程状态转换

首先我们来看看操作系统中的线程状态转换。

在现在的操作系统中,线程是被视为轻量级进程的,所以操作系统线程的状态其实和操作系统进程的状态是一致的。

4.2.1 NEW

处于NEW状态的线程此时尚未启动。这里的尚未启动指的是还没调用Thread实例的start()方法。

       /**
         * Thread state for a thread which has not yet started.
         */

  关于start()的两个引申问题

/* Java thread status for tools,
* initialized to indicate thread 'not yet started'
*/

private volatile int threadStatus = 0;
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
  //...
}

  实验下执行2次start,会发现第二次threadStatus不为0了,抛出异常。

4.2.2 RUNNABLE

  表示当前线程正在运行中。处于RUNNABLE状态的线程在Java虚拟机中运行,也有可能在等待CPU分配资源。

4.2.3 BLOCKED

阻塞状态。处于BLOCKED状态的线程正等待锁的释放以进入同步区。

我们用BLOCKED状态举个生活中的例子:

假如今天你下班后准备去食堂吃饭。你来到食堂仅有的一个窗口,发现前面已经有个人在窗口前了,此时你必须得等前面的人从窗口离开才行。
假设你是线程t2,你前面的那个人是线程t1。此时t1占有了锁(食堂唯一的窗口),t2正在等待锁的释放,所以此时t2就处于BLOCKED状态

4.2.4 WAITING

等待状态。处于等待状态的线程变成RUNNABLE状态需要其他线程唤醒。

调用如下3个方法会使线程进入等待状态:

  • Object.wait():使当前线程处于等待状态直到另一个线程唤醒它;
  • Thread.join():等待线程执行完毕,底层调用的是Object实例的wait方法;
  • LockSupport.park():除非获得调用许可,否则禁用当前线程进行线程调度。

4.2.5 TIMED_WAITING

超时等待状态。线程等待一个具体的时间,时间到后会被自动唤醒。

调用如下方法会使线程进入超时等待状态:

  • Thread.sleep(long millis):使当前线程睡眠指定时间;
  • Object.wait(long timeout):线程休眠指定时间,等待期间可以通过notify()/notifyAll()唤醒;
  • Thread.join(long millis):等待当前线程最多执行millis毫秒,如果millis为0,则会一直执行;
  • LockSupport.parkNanos(long nanos): 除非获得调用许可,否则禁用当前线程进行线程调度指定时间;
  • LockSupport.parkUntil(long deadline):同上,也是禁止线程进行调度指定时间

4.2.6 TERMINATED

终止状态。此时线程已执行完毕。

终止状态。此时线程已执行完毕。

4.3 线程状态的转换

根据上面关于线程状态的介绍我们可以得到下面的线程状态转换图:线程状态转换图

 

4.4 常用方法

  Thread类的常用方法如下:

void start();//启动线程
void sleep(long);//让线程进入睡眠,让出cpu资源,不释放锁
void yield();//让步,让出CPU资源,给予同级别和更高优先级的线程获取资源的机会,也可能自己再次获取资源
void interrupt();//设置中断标识为true
void interrupted();//第一次调用将中断标识设置为true,第二次设置为false
void isInterrupted();//判断中断标识是否为true
void join();//主线程等待调用此方法的线程执行结束
void join(long);//主线程等待调用此方法的线程执行结束/超时

  Obecjt类(涉及到锁)的常用方法如下:

void wait();//让线程进入等待,让出CPU资源,释放锁
void wait(long);
void notify();//唤醒一个(随机)在此对象监视器上等待的线程
void notifyAll();//唤醒所有在此对象监视器上等待的线程

  一个sleep/wait例子

public class TreadDemo {

    private static Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        new Thread(new HelloA()).start();
        Thread.currentThread().sleep(100);//主线程沉睡100毫秒,保证A先执行
        new Thread(new HelloB()).start();//B需要获取锁,才能执行
        new Thread(new HelloC()).start();//C无效获取锁就能执行
    }

    public static class HelloA implements Runnable{
        @Override
        public void run() {
            synchronized(lock){
                System.out.println("A进来了");
                try {
                    Thread.currentThread().sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("hello A");
            }
        }
    }

    public static class HelloB implements Runnable{
        @Override
        public void run() {
            synchronized(lock){
                System.out.println("B进来了");
            }
        }
    }

    public static class HelloC implements Runnable{
        @Override
        public void run() {
            System.out.println("C进来了");
        }
    }
}

  结果:可以看出线程A调用了sleep方法让出了CPU资源,线程C执行了,但是线程B没获得锁,需要等待线程A释放锁,所以线程A继续执行,线程B等A执行完后才执行。

A进来了
C进来了
hello A
B进来了

Process finished with exit code 0

  我们改下方法,调用Object.wait方法

    public static class HelloA implements Runnable{
        @Override
        public void run() {
            synchronized(lock){
                System.out.println("A进来了");
                try {
                    lock.wait(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("hello A");
            }
        }
    }

  结果:可以看出B获取到锁了,执行了。

A进来了
B进来了
C进来了
hello A

   线程中断

在某些情况下,我们在线程启动后发现并不需要它继续执行下去时,需要中断线程。目前在Java里还没有安全直接的方法来停止线程,但是Java提供了线程中断机制来处理需要中断线程的情况。

线程中断机制是一种协作机制。需要注意,通过中断操作并不能直接终止一个线程,而是通知需要被中断的线程自行处理。

简单介绍下Thread类里提供的关于线程中断的几个方法:

void interrupt();//设置中断标识为true
void interrupted();//第一次调用将中断标识设置为true,第二次设置为false
void isInterrupted();//判断中断标识是否为true

  

    public static void main(String[] args){
        new Thread(new ThreadA()).start();
    }

    public static class ThreadA implements Runnable{

        @Override
        public void run() {
            while (true){
                System.out.println(Thread.currentThread().isInterrupted());
                System.out.println("运行中...");
                Thread.currentThread().interrupt();//只是设置标志位而已,不能停止线程
            }
        }
    }
}

  

  public static class ThreadA implements Runnable{

        @Override
        public void run() {
            int i = 0;
            while (true){
                System.out.println(Thread.currentThread().isInterrupted());
                System.out.println("运行中...");
                Thread.currentThread().interrupt();
                try {
                    if(i == 10){
                        Thread.currentThread().interrupt();//设置退出标识
                    }
                    Thread.currentThread().sleep(1000);//sleep根据中断标识位为true抛出异常
                    i++;
                } catch (InterruptedException e) {
                    break;//捕获interruptException,退出循环
                }
            }
            System.out.println("线程结束");//线程退出了
        }
    }

  yield方法例子,结果:线程A的其他线程未执行:“我进来了”,证明线程A的锁没释放。线程B打印了,说明线程A的CPU资源有释放。

public class ThreadYieldDemo {

    private static Object lock = new Object();

    public static void main(String[] args){
        for(int i = 0; i < 10; i++){
            new Thread(new ThreadA()).start();
        }
        new Thread(new ThreadB()).start();
    }
    public static class ThreadA implements Runnable{
        @Override
        public void run() {
            synchronized(lock){
                System.out.println("我进来了");
                Thread.currentThread().yield();
                System.out.println("我没放弃锁啊");
                while (true){

                }
            }
        }
    }
    public static class ThreadB implements Runnable{
        @Override
        public void run() {
            try {
                Thread.currentThread().sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int i = 10;
            do {
                System.out.println("我是另一个哈");
                i--;
            }while (i == 0);
        }
    }
}

  

4.5 守护线程与用户线程

    守护线程:中后台提供通用性支持的线程,比如垃圾回收线程。

  用户线程:用户的业务线程。

  区别:守护线程是最低优先级的,它不持有任何的系统资源,当所有的非守护线程结束的时候,程序也就终止了。

     用户线程中start前,可以调用setDaemon方法来转换为守护线程。守护线程创建的子线程也是守护线程。

  

原文地址:http://redspider.group:4000/article/01/4.html

感恩作者

 

posted @ 2020-11-21 17:53  knbsyoo  阅读(211)  评论(0)    收藏  举报