写于:2019-11-01

线程存在,就是为了让程序能够并行的执行。并行指的就是一系列任务在计算中同时运行的行为。如:浏览网页的同时播放音乐等。

# 一、线程简介

# 1、概念

wiki百科-线程

# 2、一个简单的线程案例

模拟:同时浏览网页,同时听歌。

# 代码

public class SimpleThread {
    public static void main(String[] args) {
        new Thread(()->{
            while (true){
                System.out.println("======听音乐======");
                sleep(1);
            }
        },"listen-music").start();
        while (true){
            System.out.println("======浏览网页======");
            sleep(1);
        }
    }

    public static void sleep(long mill){
        try {
            Thread.sleep(mill);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

# 控制台打印

======浏览网页======
======听音乐======
======浏览网页======
======听音乐======
======浏览网页======
======听音乐======
======听音乐======
.....................................

# jconsole 查看线程

jconsole线程图

小贴士:

1、main 方法启动时,其本身就是一个线程。

2、Thread 线程,只有调用了 Thread#start 方法,才代表派生出了一个新的线程。

# 二、线程的生命周期

线程生命周期图

从图中可知,线程的生命周期大致分为5个主要阶段

# 1、NEW

new Thread();

new 一个 Thread 对象,此时线程处于 NEW 状态。它只是一个普通的对象,在没有调用 start 方法前,该线程根本不存在。

# 2、RUNNABLE

调用 Thread#start() 方法。

调用 start 方法后,真正的在 JVM 中创建了一个线程,此时线程并不能马上执行,需要根据 CPU 的调度才能执行,此时属于等待执行的状态,叫做:RUNNABLE 状态【可运行状态】。

RUNNABLE 只能进入 RUNNING 状态,或者意外终止。调用 wait、sleep或其他 block 的 IO 操作,也需要先获取CPU 执行权,也就是先进入 RUNNING 状态。

# 3、RUNNING

当 CPU 空闲并从任务可执行队列中选中线程,此时线程获取到的 CPU 执行权,此时线程处于 RUNNING 状态。

在 RUNNING 状态时,可能存在的状态变更

1、直接进入 TERMINATED 状态,如:JDK stop 方法,获取存在某个判断结束的标识。

2、进入 BLOCK 状态。如:调用 sleep,或 wait 方法将线程加入 waitSet 中。

3、进入某个阻塞的IO操作。如:因网络数据读写而进入 BLOCKED 状态。

4、获取某个锁,从而加入该锁阻塞的队列中从而进入 BLOCKED 状态。

5、CPU 时间片执行完,CPU 放弃执行该线程,线程进入 RUNNABLE 状态。

6、主动调用 yield 方法,放弃 CPU 执行权,进入 RUNNABLE 状态。

# 4、BLOCKED

线程因为调用 sleep、wait方法,进入某个阻塞的 IO 操作或者争抢锁资源而进入 BLOCKED 状态。

在 BLOCKED 状态时,可能存在的状态变更

1、直接进入 TERMINATED。如:JDK stop 方法,或者意外死亡。

2、阻塞的 IO 操作结束。如:读取数据结束进入 RUNNABLE 状态。

3、线程完成指定时间的休眠,进入到 RUNNABLE 状态。

4、wait 中的线程被其他线程 notify/notifyAll 唤醒,进入 RUNNABLE 状态

5、线程获取到某个锁资源,进入 RUNNABLE 状态。

6、线程在阻塞过程中被打断,比如其他线程的 interrupt 方法,进入 RUNNABLE 状态。

# 5、TERMINATED

线程的最终状态,进入该状态,表示线程的生命周期结束。

会进入该状态的操作:

1、线程正常结束,结束生命周期

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

3、JVM Crash,导致所有的线程都结束了。

# 三、Runnable 接口

# 1、源码分析 Runnable 接口

小贴士:在 JDK 中代表线程的只有 Thread 这个类。

Runnable 是一个简单的接口,只定义了一个无参无返回值的方法,代码如下:

@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();
}

首先、Runnable 是一个函数式接口,可以使用 java8 lambda 表达式。

其次、通过 Runnable#run() 的 java doc 我们可以得知,Runnable#run 会在线程 start 的使用被 Thread#run 调用。

下面我们来看看 Thread#run 方法代码

public class Thread implements Runnable {
	/* What will be run. */
	// 构造函数传入的 Runnable ,如果没有传入则为空
    private Runnable target;

    @Override
    public void run() {
    	// 如果存在传入的 Runnable 实现,会执行 Runnable#run 方法
        if (target != null) {
            target.run();
        }
        // 如果 target 为空,则需要重写该方法,写入业务逻辑
    }
}

# 2、Runnable 接口 对比重写 Thread#run

1、重写 Thread#run 方法是,业务逻辑实在 run 方法中的,换句话说就是,业务逻辑和 Thread 是耦合的。换句话说就是多线程间的 run 方法是不能共享的,也就是线程A不能把线程B的run方法当做自己的执行单元。

2、使用 Runnable 接口,能够将执行单元和 Thread 的操作进行解耦,同时多个线程在构造时能够使用同一套执行单元。

精彩内容推送,请关注公众号!
最近更新时间: 4/16/2020, 8:37:50 PM