【多线程】程序猿进阶多线程(三)—— 线程的运行状态

一、前言

      在上一篇博客中,小编向大家介绍了 线程创建和启动,简单的向大家介绍了线程创建和启动的三种方法:1.继承Thread类,2.实现Runnable接口,3.使用Future和Task创建。可能大家对线程的初步创建有了一定的了解。

      在这篇博客中,小编向大家介绍一下,线程运行起来后,有的状态。

二、线程的状态

这里写图片描述

      线程在执行过程中,可以处于下面几种状态:

就绪(Runnable):线程准备运行,不一定立马就能开始执行。
运行中(Running):进程正在执行线程的代码。
等待中(Waiting):线程处于阻塞的状态,等待外部的处理结束。
阻塞状态(Blocked):等待 I/O 操作完成。
同步阻塞(Locking):等待获取锁。
死亡(Dead):线程完成了执行。

      下面详细说明:

  • 就绪状态 - Runnable

      当我们创建好线程后,Thread t = new Thread();,开始调用t.start();方法后,线程就从初始状态转变为就绪状态。

      就绪状态下的线程,在等待操作系统调用,操作系统会根据不同的调度算法来进行调度线程。比如操作系统使用时间片轮转算法。

  • 运行状态 - Running

      当操作系统选定要运行的线程后,这个线程就会从就绪状态转为运行状态。这个时候,运行状态的线程就会执行我们重写的run()方法。

  • 阻塞状态 - blocked

      有的时候,我们需要给线程之间一些缓冲时间,通常使用Sleep()让子线程和主线程错开。有的时候我们需要线程按照一定的顺序执行,这个时候我们可以使用b.join(),安排在线程b执行完成后再执行。

      所以在sleep()join()调用的过程中,线程会处于阻塞状态。只有等sleep()join()完成后,线程才会再次进入就绪状态,等待cpu调用。

  • 等待状态 - waiting

      运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒

  • 锁池状态 - locking

      当执行中的线程进入synchronized同步块的时候,没有获取到锁的线程,就会进入锁池状态,获取到锁的线程执行完成后,锁解除,进入就绪状态。

  • 死亡状态 -dead

      线程执行完成,进入死亡状态。

三、一些知识点

      在上面的线程状态图中,里面有很多方法,可能不是很清楚。下面小编通过几个问题来对比记忆一下这些方法:

      java多线程中,会有很多问题,比如经典的:

3.1 现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行?

【方法一】

      这个简单的问题,我们可以直接使用join函数完成,join的作用就是,b.join()在b线程执行完成后再执行。这样就保证了执行顺序。

package com.dmsd.thread;

/**
 * Created by Ares on 2018/6/11.
 *
 * 说明:使用join完成 ————t1执行完了,t2执行;t2执行完成后,t3执行。
 *
 * 假设现在有两个线程A、B。如果在A的run方法中调用B.join(),
 * 表示A需要在B线程上面等待,也就是需要在B线程执行完成之后才能再次执行。
 */
public class join {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(40);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1 run:"+i);
            }

        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(40);
                    t1.join();//表明当前线程需要在t1线程上等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t2 run:"+i);
            }

        });
        Thread t3 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(40);
                    t2.join();//表明当前线程需要在t2线程上等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t3 run:"+i);
            }

        });
        t1.start();
        t2.start();
        t3.start();
    }
}

【方法二】

      可以说使用join的老铁,还是比较low的。在我们的juc包( java.util.concurrent)中,利用ExcutorService产生的newSingleThreadExecutor的单一线程池。这个线程池的底层是使用了先进先出的队列,通过submit依次把线程添加进队列,然后顺序执行。

package com.dmsd.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by Ares on 2018/8/14.
 */
public class TestSingleThreadPool  {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        Thread t1 = new Thread(new MyThread1());
        Thread t2 = new Thread(new MyThread2());
        Thread t3 = new Thread(new MyThread3());

        executorService.submit(t1);
        executorService.submit(t2);
        executorService.submit(t3);

        executorService.shutdown();

    }
}

class MyThread1 implements Runnable {
    @Override
    public void run() {
        System.out.println("I am thread 1");
    }
}

class MyThread2 implements Runnable {
    @Override
    public void run() {
        System.out.println("I am thread 2");
    }
}

class MyThread3 implements Runnable {
    @Override
    public void run() {
        System.out.println("I am thread 3");
    }
}

      底层中用了newThreadPoolExecutor()创建了线程池,线程池中一个线程,并把线程放到linkedBlockingQueue阻塞队列中。保证了线程的顺序执行。

      源码:

 public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

3.2 Java中sleep和wait的区别 ?

      这个问题也是经常问的问题。

① 这两个方法来自 不同的类 分别是,sleep来自Thread类,和wait来自Object类。
sleep是Thread的静态类方法, 谁调用的谁去睡觉,即使在a线程里调用b的sleep方法,实际上还是a去睡觉, 要让b线程睡觉要在b的代码中调用sleep。

② 锁: 最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。

      sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源,其他线程可以占用CPU。一般wait不会加时间限制,因为如果wait线程的运行资源不够,再出来也没用,要等待其他线程调用notify/notifyAll唤醒等待池中的所有线程,才会进入就绪队列等待OS分配系统资源。sleep(milliseconds)可以用时间指定使它自动唤醒过来,如果时间不到只能调用interrupt()强行打断。

      Thread.sleep(0)的作用是“触发操作系统立刻重新进行一次CPU竞争”。

③ 使用范围:wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用。

   synchronized(x){ 
      x.notify() 
     //或者wait() 
   }

四、小结

      通过线程的状态,详细大家可以更加深入的剥开多线程的面纱,对多线程的理解也会更加的深刻。

猜你喜欢

转载自blog.csdn.net/kisscatforever/article/details/81661887
今日推荐