乐趣区

关于java:Thread状态流转方法使用原理分析

积淀、分享、成长,让本人和别人都能有所播种!😄

一、Thread 状态

Java的线程状态形容在 Thread 类外面的枚举类 State 中,包门路为java.lang.Thread.State,总共蕴含以下六种状态:NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED

public enum State {
    // 尚未启动的线程的线程状态
    NEW,
    
    // 可运行线程的线程状态,是可运行的线程
    // 这个状态在 Java 虚拟机中进行,但它可能期待来自操作系统的其余资源,比方处理器
    RUNNABLE,
    
    // 线程阻塞期待监视器锁的线程状态
    // 处于阻塞状态的线程正在期待监视器锁
    // 例如 synchronized 锁
    BLOCKED,
    
    // 期待线程的线程状态
    // 线程调用以下办法会处于期待状态:Object.wait()不超时、Thread.join()不超时、LockSupport.park()
    // 一个处于期待状态的线程正在期待另一个线程执行特定动作,例如:// 一个线程调用了 Object.wait()办法在一个对象上正在期待另一个线程调用 Object.nofify()或者
    // Object.nofifyAll()办法开启那个对象
    // 一个调用了 Thread.join()办法的线程正在期待指定线程终止
    WAITING,
    
    // 具备指定等待时间的期待线程的线程状态,调用一下办法会处于这个状态:// Object.wait() 超时、Thread.join()超时
    // LockSupport.parkNanos()、LockSupport.parkUntil()
    TIMED_WAITING,
    
    // 已终止线程的线程状态
    // 线程已执行结束或者产生异样终止
    TERMINATED
}

下面的六种状态相当于形容了一个线程的生命周期,这些状态之间是能够流转的,那么线程的状态是如何流转的呢,咱们看下图:

  • NEW:未启动状态。

    public static void main(String[] args) {Thread t = new Thread(){
            @Override
            public void run() {System.out.println(Thread.currentThread().getName());
            }
        };
        // 这个时候只是 new 了一个线程,并没有调用 start()办法
        // 输入 NEW
        System.out.println(t.getState().name());
    }
  • RUNNABLE:可运行状态,处于可运行状态的线程正在 Java 虚拟机中运行,但也可能是正在期待来自操作系统资源,比方 CPU。在 RUNNABLE 状态中蕴含了 RUNNINGREADY 两个状态,这两个状态是能够相互流转的。当线程调用 start() 办法后,线程就处于 READY 状态,期待操作系统调配 CPU 工夫片,调配后进入 RUNNING 状态。当调用 yield() 办法后,只是谦让的容许以后线程让出 CPU,然而不肯定让,由操作系统决定,如果让了以后线程就会进入READY 状态,期待零碎调配 CPU 工夫片再次进入 RUNNING 状态。

    public static void main(String[] args) {Thread t = new Thread(){
            @Override
            public void run() {System.out.println(Thread.currentThread().getName());
            }
        };
        t.start();
        // 调用 start()办法
        // 输入 RUNNABLE
        System.out.println(t.getState().name());
    }
  • BLOCKED:阻塞状态。当产生线程锁竞争状态下,没有获取到锁的线程会被挂起进入阻塞状态,比方 synchronized 锁。

    public static void main(String[] args) throws InterruptedException {final Object lock = new Object();
    
        Thread t = new Thread(){
            @Override
            public void run() {synchronized (lock){System.out.println(Thread.currentThread().getName());
                }
            }
        };
        // main 线程先获取锁,t 线程 start 的时候进入阻塞状态
        synchronized (lock) {t.start();
            Thread.sleep(1000);
            // 输入 BLOCKED
            System.out.println(t.getState().name());
        }
    }
  • WIITING:期待状态,可被唤醒的期待状态,此时线程不会执行也不会被调度,:可被唤醒的期待状态,此时线程不会被执行也不会被系统调度,此状态能够通过 synchronized 取得锁,调用 wait 办法进入期待状态,最初通过 Object.notify()Object.nofifyAll() 唤醒。下列办法能够让线程进入 WAITING 状态:Object.wait()Thread.join()LockSupport.park(),调用以下办法能够唤醒登台的线程进入 RUNNABLE 状态:Object.notify()Object.nofifyAll()LockSupport.unpark(Thread)

    public static void main(String[] args) throws Exception {final Object lock = new Object();
        Thread t = new Thread(){
            @Override
            public void run(){
                try {synchronized (lock) {lock.wait();
                        System.out.println(Thread.currentThread().getName());
                    }
                } catch (InterruptedException e) {e.printStackTrace();
                }
            }
        };
        t.start();
        Thread.sleep(1000);
        synchronized (lock) {
            // 输入 WAITING
            System.out.println(t.getState().name());
            lock.notify();}
    }
  • TIMED_WAITING:具备等待时间的期待状态,此时线程不会执行也不会被调度,直到等待时间到期后才会被执行。下列办法能够让线程进入 TIMED_WAITING 状态:Thread.sleep(long)Object.wait(long)Thread.join(long)LockSupport.parkNanos()LockSupport.parkUntil()。除了等待时间到期会被执行外,还能够调用以下办法:Object.notify()Object.nofifyAll()LockSupport.unpark(Thread)

    public static void main(String[] args) throws Exception {Thread t = new Thread(() ->{
            try {Thread.sleep(10000);
            } catch (InterruptedException e) {e.printStackTrace();
            }
        });
        t.start();
        Thread.sleep(1000);
        // 输入 TIMED_WAITING
        System.out.println(t.getState().name());
    }
  • TERMINATED:已终止状态,线程已实现执行或者产生了异样终止。

    public static void main(String[] args) {Thread t = new Thread(){
            @Override
            public void run() {System.out.println(Thread.currentThread().getName());
            }
        };
        t.start();
        // 输入 TERMINATED
        System.out.println(t.getState().name());
    }

二、创立 Thread 形式

创立线程的形式有三种,别离是:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和应用Future

继承 Thread

public class MyThread extends Thread {
    @Override
    public void run() {// do something}
}

实现 Runnable

public class MyThread implements Runnable {
    @Override
    public void run() {// do something}
}

实现 Callable 接口和应用 Future

public class ThreadDemo implements Callable {
    
    @Override
    public String call() {return "str";}

    public static void main(String[] args) throws Exception {ThreadDemo threadDemo = new ThreadDemo();
        FutureTask<String> future = new FutureTask<String>(threadDemo);
        Thread thread = new Thread(future);
        thread.start();
        System.out.println(future.get());
    }
}

三、Thread 办法应用

Thread类罕用的办法有以下:

  • start():启动以后线程。
  • run():线程启动的时候执行的办法。
  • currentThread():静态方法,返回执行以后代码的线程。
  • getName():获取以后线程的名字。
  • setName(String name):设置以后线程的名字
  • yield():开释以后 CPU 的执行权(但也有可能下一刻的执行权又回到了以后线程,主控权还是在 CPU 手上)。
  • join():当线程 a 调用线程 b 的 join() 办法,此时线程 a 进入阻塞状态,直到线程 b 齐全执行完之后,线程 a 完结阻塞状态。
  • stop:当执行此办法时,强制完结以后线程(已停用)。
  • sleep():让以后线程“睡眠”指定的 millitime毫秒。在指定的 millitime 毫秒工夫内,以后过程是阻塞状态。
  • getPriority():获取线程优先级。
  • isAlive():判断以后线程是否存活(线程执行完之前都是存活的)。
  • setDeamon():设置守护线程。
  • isDaemon():是否是守护线程。
  • isInterrupted():线程是否中断。

守护线程

守护线程又称后盾线程,默认创立进去的线程都是前台线程,能够通过 setDeamon(true) 设定为后盾线程。

后盾线程和前台线程次要辨别在完结机会:当一个过程中的所有前台线程都执行结束,无论过程中的后盾线程是否执行结束,都要强制中断。

看以下例子:

public static void main(String[] args) {Thread rose = new Thread(() ->{for (int i = 0; i < 10; i++) {
            try {Thread.sleep(1000);
            } catch (InterruptedException e) {e.printStackTrace();
            }
            System.out.println("Rose: 啊啊啊啊啊 AAAAAaaaaa......");
            System.out.println("成果:噗通......");
        }
    });

    Thread jack = new Thread(() ->{while(true){
            try {Thread.sleep(1000);
            } catch (InterruptedException e) { }
            System.out.println("Jack:You jump,I jump");
        }
    });

    // jack 线程设置为后盾线程, 肯定要在 start()办法之前调用
    jack.setDaemon(true);

    rose.start();;
    jack.start();

    //while(true);// 有过程没完结,后盾线程不会完结。}

下面代码输入如下:

Jack:You jump,I jump
Rose:Let me go die!
Rose:Let me go die!
Jack:You jump,I jump
Jack:You jump,I jump
Rose:Let me go die!
Jack:You jump,I jump
Rose:Let me go die!
Rose:Let me go die!
Jack:You jump,I jump
Jack:You jump,I jump
Rose:Let me go die!
Jack:You jump,I jump
Rose:Let me go die!
Jack:You jump,I jump
Rose:Let me go die!
Rose:Let me go die!
Jack:You jump,I jump
Jack:You jump,I jump
Rose:Let me go die!
Rose: 啊啊啊啊啊 AAAAAaaaaa......
成果:噗通......

从输入能够看出前台线程执行结束之后,后盾线程就立刻进行了,如果把 while(true) 去掉正文,那么后盾线程会始终执行。

sleep

sleep()办法能够使以后办法的线程阻塞指定毫秒,超时后,该线程办法会主动回到 RUNNABLE 状态期待再次调配工夫片执行。

public static void main(String[] args) {System.out.println("程序开始了");
    try{
        /*
             * 这里会使 main 线程阻塞 5 秒,在此过程中
             * 若其余线程调用了 main 线程的 interrupt 办法
             * 试图中断 main 线程时,sleep 办法会抛出异样
             */
        Thread.sleep(5000);
    }catch(InterruptedException e){ }
    System.out.println("程序完结了");
}

join

join()办法会阻塞调用以后办法的线程,并在 join() 办法所属对象线程上期待,直到该线程执行结束才会解除阻塞继续执行。

举个栗子:a 线程调用 b 线程的 join() 办法,那么 a 线程就会进入阻塞状态,直到 b 线程执行结束才会解除阻塞持续往下执行。

public static void main(String[] args) {

        // 下载图片线程
        final Thread download = new Thread(() ->{System.out.println("download:开始下载照片");
            for (int i = 1; i <= 100; i++) {System.out.println("下载进度:"+i+"%");
            }
            try {Thread.sleep(500);
            } catch (InterruptedException e) {e.printStackTrace();
            }
            System.out.println("download:图片下载结束");
        });

        // 显示图片线程
        final Thread show = new Thread(() ->{System.out.println("show:开始显示图片");
            try {
                // 只有图片下载结束之后能力显示图片
                // 调用下载图片线程的 join()办法阻塞显示图片线程直到图片下载结束
                download.join();} catch (InterruptedException e) {e.printStackTrace();
            }
            System.out.println("show:图片显示结束");
        });
        download.start();
        show.start();}

下面代码输入如下:

show:开始显示图片
download:开始下载照片
下载进度:1%
下载进度:...
下载进度:100%
download:图片下载结束
show:图片显示结束

wait & nofify

wait()notify() 办法是在 Object 类定义的,用来协调线程同步应用,相比 join() 同步的及时性更强,因为 join() 办法必须期待另外一个线程的所有工作都完结。

public static boolean isFinish;
public static Object obj = new Object();
public static void main(String[] args) {
    // 下载图片线程
    final Thread download = new Thread(() ->{System.out.println("download:开始下载照片");
        for (int i = 1; i <= 100; i++) {System.out.println("down 图片进度:"+i+"%");
        }
        try {Thread.sleep(500);
        } catch (InterruptedException e) {e.printStackTrace();
        }
        System.out.println("download:图片下载结束");
        isFinish = true;
        // 图片下载结束就能够告诉显示图片线程显示图片, 没必要等下载附件执行结束
        synchronized (obj) {obj.notify();
        }

        // 开始下载附件
        System.out.println("down:开始下载附件...");
        for (int i = 1; i <= 100; i++) {System.out.println("down 附件进度:" + i + "%");
            try {Thread.sleep(50);
            } catch (InterruptedException e) {}}
        System.out.println("down:附件下载结束!");
    });

    // 显示图片线程
    final Thread show = new Thread(() ->{System.out.println("show:开始显示图片");
        try {// obj.wait()办法会导致 show 线程进入阻塞状态直到 obj 调用 notify()办法
            synchronized (obj) {obj.wait();
            }
        } catch (InterruptedException e) {e.printStackTrace();
        }
        if (!isFinish) {throw new RuntimeException("图片不存在!");
        }
        System.out.println("show:图片显示结束");
    });
    download.start();
    show.start();}

下面输入如下:

show:开始显示图片
download:开始下载照片
down 图片进度:1%
down 图片进度:...
down 图片进度:100%
download:图片下载结束
down:开始下载附件...
show:图片显示结束
down 附件进度:1%
down 附件进度:...
down 附件进度:100%
down:附件下载结束!

从下面输入咱们看出当下载线程图片下载结束之后,显示图片线程就执行了,不须要期待附件下载结束再执行。

Oject还有一个 notifyAll() 办法,当该办法被调用时,期待队列中的所有对象都被会唤醒。

yield

yield()办法会使线程让出 CPU,但不是暂停执行,线程还会持续进行CPU 的抢夺,然而也不是肯定就会让出,这个由操作系统决定。如果一个线程不那么重要,或者对应工作的优先级非常低,或者不心愿它占用过多的资源,那么就能够应用 yield() 办法给予其它线程更多的执行机会。

sleep 和 wait 的区别

  • wait() 办法阻塞的线程能够通过 notify() 或者 notifyAll() 办法唤醒,而执行 sleep(0 办法的线程只能始终休眠到指定工夫,不可被唤醒。
  • 当被 wait() 办法阻塞的线程被唤醒的时候,会开释指标对象的锁,而 sleep() 办法指定的睡眠时候到的时候不会开释任何资源。
退出移动版