一、JAVA多线程:快速认识线程 (NEW、RUNNABLE、RUNNING、BLOCKED、TERMINATED)

      本章主要介绍线程的概念,以及线程在Java中的主要作用,并且详细讲解了线程的生命周期,以及生命周期每个状态之间的切换方法。

线程介绍

对计算机来说,每一个任务都是一个进程,每个进程内存至少有一个线程(Thread)是在运行中,有时线程也成为轻量级的进程。

线程是程序执行的一个路径,每一个线程都有自己的局部变量表、程序计数器(指向正在执行的指令指针)以及各自的生命周期,现代操作系统中一般都不止一个线程在运行,当启动了一个Java虚拟机(JVM)时,从操作系统开始就会创建一个新的进程(JVM进程),JVM进程中将会派生或者创建很多线程。

代码样式


/**
 *  创建一个多线程实例
 */
public class TryConcurrency {


    public static void main(String[] args) {
        // 通过匿名内部类的方式创建线程,并重写run方法
//        new Thread(){
//
//           @Override
//           public void run(){
//               browseNews();
//
//           }
//
//        }.start();
//
//        enjoyMusic();

        // Java8 Lambda改造上面的代码

        new Thread(TryConcurrency::enjoyMusic).start();

        browseNews();


    }


    private static void  browseNews()   {
        for (;;){
            System.out.println("Uh-huh,the good news .");
            sleep(1);
        }
    }



    private static void enjoyMusic()    {
        for (;;){
            System.out.println("Uh-huh,enjoy music .");
            sleep(1);
        }
    }


    private static void sleep(int seconds) {
        try {
            TimeUnit.SECONDS.sleep(seconds);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


}

运行截图:

线程的声明周期讲解

接下来,依次进行讲解

1.线程的NEW状态

用关键new创建一个Thread对象时,此时它并不处于执行状态,因为没有调用start方法启动该线程,那么线程的状态为NEW状态。

它只是Thread对象的状态,在没有start之前,该线程根本不存在。

NEW状态通过start方法进入RUNNABLE状态

2.线程的RUNNABLE状态

线程对象进入RUNNABLE状态必须调用start方法,此时才是真正的在JVM进程中创建了一个线程,线程一经启动并不一定立即执行。要听令于CPU调度。 我们把这个中间状态成为可执行状态(RUNNABLE),具备执行的资格。并没有真正执行而是等待CPU调度

只能进入RUNNING状态或者意外终止

3.线程的RUNNING状态

CPU通过轮询或者其他方式从任务可执行队列中选中了线程,才真正执行自己的逻辑代码

一个正在RUNNING状态的线程事实上也是RUNNABLE的,反过来则不成立。

可以进入的状态(三种):

TERMINATED: 调用JDK已经不推荐使用的stop方法或者判断某个逻辑标识

BLOCKED : 调用sleep或wait方法加入waitSet中

                       进行某个阻塞的IO操作

                       获取某个锁资源

RUNNABLE: 由于CPU的调度器轮询使该线程放弃执行

                       主动调用yield方法,放弃CPU执行权

                       

线程的BLOCKED状态

进入该状态原因(三种):

                       1.调用sleep或wait方法加入waitSet中

                       2.进行某个阻塞的IO操作

                       3.获取某个锁资源

可以进入的状态 :

TERMINATED : 调用JDK已经不推荐使用的stop方法或意外死亡(JVM Crash)

RUNNABLE : 线程阻塞的操作结束,比如读取到了想要的数据字节

                       完成指定时间的休眠

                       Wait中的线程被其他线程notify/notifyall唤醒

                       获取到了某个锁资源

                       线程阻塞过程中被打断,调用了interrupt方法

线程的TERMINATED状态

TERMINATED是一个线程的最终状态,不会切换到其他任何状态,代表线程的整个声明周期都结束了

进入该状态原因(三种):

                       1.线程运行正常结束,结束生命周期

                       2.线程运行出错意外结束

                       3.JVM Crash, 导致所有的线程都结束

线程的start方法剖析:模板设计模式在Thread中的应用

Thread类中的run和start是一个比较经典的模板设计模式,父类编写算法结构代码,子类实现具体逻辑

start方法

分析Thread的start方法,在调用了start方法之后到底进行了什么操作。

/**
 * Causes this thread to begin execution; the Java Virtual Machine
 * calls the <code>run</code> method of this thread.
 * <p>
 * The result is that two threads are running concurrently: the
 * current thread (which returns from the call to the
 * <code>start</code> method) and the other thread (which executes its
 * <code>run</code> method).
 * <p>
 * It is never legal to start a thread more than once.
 * In particular, a thread may not be restarted once it has completed
 * execution.
 *
 * @exception  IllegalThreadStateException  if the thread was already
 *               started.
 * @see        #run()
 * @see        #stop()
 */
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();

    /* Notify the group that this thread is about to be started
     * so that it can be added to the group's list of threads
     * and the group's unstarted count can be decremented. */
    group.add(this);

    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
              it will be passed up the call stack */
        }
    }
}

在这里,最核心的就是start0 本地方法,也是JNI方法

private native void start0();

当开始执行这个线程的时候,JVM会调用该线程的run方法。换而言之。 run方法就是被JNI的start0方法调用的

1.Thread被构造的后的NEW状态,threadStatus这个内部状态为0

2.不能两次启动Thread,只有第一个线程在运行。第二个线程失败 Exception in thread "main" java.lang.IllegalThreadStateException

3.线程启动,会加入ThreadGroup中

4.TERMINATED无法再调用start方法,无法回归到其他状态。

run方法

线程的执行单元

如果,没有使用runnable接口对其进行构造,那么run方法本事就是一个空的实现:

/**
 * If this thread was constructed using a separate
 * <code>Runnable</code> run object, then that
 * <code>Runnable</code> object's <code>run</code> method is called;
 * otherwise, this method does nothing and returns.
 * <p>
 * Subclasses of <code>Thread</code> should override this method.
 *
 * @see     #start()
 * @see     #stop()
 * @see     #Thread(ThreadGroup, Runnable, String)
 */
@Override
public void run() {
    if (target != null) {
        target.run();
    }
}

实例:模拟营业大厅叫号程序 (存在线程安全问题,后续会讲解)

四个线程工作,每天最多出50个号码

TicketWindow代表出号机

public class TicketWindow extends Thread {


    public static void main(String[] args) {
        TicketWindow t1 = new TicketWindow("一") ;
        t1.start();

        TicketWindow t2 = new TicketWindow("二") ;
        t2.start();

        TicketWindow t3 = new TicketWindow("三") ;
        t3.start();

        TicketWindow t4 = new TicketWindow("四") ;
        t4.start();

    }

    private final  String name ;

    private static final int MAX = 50 ;

    private static int index = 1 ;

    public  TicketWindow(String name){
        this.name = name ;
    }

    @Override
    public void run(){
        while (index <= MAX ) {
            System.out.println("柜台:"+name +" 当前叫号" + ( index++ ) );
        }
    }


}

Runnable接口的引入以及策略模式在Thread中的使用

/**
 * The <code>Runnable</code> interface should be implemented by any
 * class whose instances are intended to be executed by a thread. The
 * class must define a method of no arguments called <code>run</code>.
 * <p>
 * This interface is designed to provide a common protocol for objects that
 * wish to execute code while they are active. For example,
 * <code>Runnable</code> is implemented by class <code>Thread</code>.
 * Being active simply means that a thread has been started and has not
 * yet been stopped.
 * <p>
 * In addition, <code>Runnable</code> provides the means for a class to be
 * active while not subclassing <code>Thread</code>. A class that implements
 * <code>Runnable</code> can run without subclassing <code>Thread</code>
 * by instantiating a <code>Thread</code> instance and passing itself in
 * as the target.  In most cases, the <code>Runnable</code> interface should
 * be used if you are only planning to override the <code>run()</code>
 * method and no other <code>Thread</code> methods.
 * This is important because classes should not be subclassed
 * unless the programmer intends on modifying or enhancing the fundamental
 * behavior of the class.
 *
 * @author  Arthur van Hoff
 * @see     java.lang.Thread
 * @see     java.util.concurrent.Callable
 * @since   JDK1.0
 */
@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

只定义了一个无参数,无返回值的run方法

策略模式在Thread中的应用

本文来源于:

《Java高并发编程详解:多线程与架构设计》 --汪文君

猜你喜欢

转载自blog.csdn.net/zhanglong_4444/article/details/85775834