天天看点

JAVA线程浅剖析

目录

什么是线程(百度百科)

操作系统(OS)线程状态

java创建线程的方法

JVM线程状态转换

线程数据结构

备注

什么是线程(百度百科)

        线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。

操作系统(OS)线程状态

JAVA线程浅剖析
OS线程的状态是以CPU资源为核心定义(忽略硬盘、网卡等资源):
  • 开始状态(new):产生一个Thread对象就生成一个新线程。当线程处于"新线程"状态时,仅仅是一个空线程对象,它还没有分配到系统资源,因此只能启动或终止它
  • 就绪状态(ready):start()方法产生运行线程所必须的资源,调度线程执行,此时并未真正执行,需要和其他线程竞争cpu资源
  • 运行状态(running):start()方法之后线程竞争到了cup资源进入运行状态
  • 等待状态(waiting):线程让出cup使用权,暂时停止运行的状态
(1)等待阻塞:运行的线程执行wait()方法,该线程会释放占用的所有资源,进入这个状态后, 
     是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒,
(2)同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用。
(3)其他阻塞:运行的线程执行sleep(…)或join(…)方法,或者发出了I/O请求时会把该线程置为阻塞态,
    当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
           
  • 结束状态(terminated):线程正常或异常结束进入死亡态

java创建线程的方法

  • 方法:①继承Thread  ②实现Runnable ③实现Callable接口
  • 涉及对象:Thread、Runnable、RunnableFuture、Future、Callable、FutureTask
  • 本质:以上3个创建线程方法的本质还是使用构造函数new Thread(…),只不过否传参Runnale不同罢了....
JAVA线程浅剖析
JAVA线程浅剖析
代码如下:new Thread(){}这种写法相当于是创建一个继承Thread的匿名内部类
  • 实现Runnable方式:①线程任务和线程对象的解耦 ②多个线程对象可处理同一个任务,方便资源共享
  • 实现Callable方式:与Runnable相比可以通过Future拿到返回结果或者异常
@Test
public void testThreadCreat() throws ExecutionException, InterruptedException {
    log.info("我是主线程:{}", Thread.currentThread().getName());

    //继承Thread: new Thread(){...}同创建匿名内部类
    Thread thread1 = new Thread("yxf-继承Thread") {
        @Override
        public void run() {
            log.info("我是子线程-:{}", Thread.currentThread().getName());
        }
    };
    thread1.start();

    //实现runnable
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            log.info("我是子线程-:{}", Thread.currentThread().getName());
        }
    };
    Thread thread2 = new Thread(runnable, "yxf-实现Runnable");
    thread2.start();

    //实现Callable
    Callable<String> callable = new Callable<String>() {
        @Override
        public String call() throws Exception {
            return "我是子线程:" + Thread.currentThread().getName();
        }
    };

    FutureTask<String> task = new FutureTask<String>(callable);
    Thread thread3 = new Thread(task, "yxf-实现Callable");
    thread3.start();
    log.info(task.get());
}
           
代码执行结果
JAVA线程浅剖析

JVM线程状态转换

JVM的线程状态不同于OS的状态
  • OS是围绕cup定义,JVM围绕是其对象本身状态,对cup的ready->running这种时间分片轮转不关注
  • JVM里的waiting更详细分为waiting、timed_waiting
  • JVM无running状态,runnable 替代OS中的ready,running和部分waiting(OS中I/O阻塞为waiting状态,而JVM中则跳转到runnable状态)
JVM的6种状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED
JAVA线程浅剖析
/**
 * 定义一个myThread继承Thread
 */
class myThread extends Thread{

    public myThread(String name) {
        super(name);
    }

    @Override
    public void run(){
        try {
            //todo 业务逻辑
            sleep(2000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("运行中{},的状态:{}", this.getName(), this.getState());
    }
}
           
首先我们测试下正常的状态流程:new-runnable-terminated
/**
 * 线程状态转换测试
 *     new-runable-terminated
 */
@Test
public void testState1() throws InterruptedException {
    //new:初始化线程后为开始状态,此时线程不会获得cpu资源
    myThread thread = new myThread("线程...A");

    log.info("{},的状态:{}", thread.getName(), thread.getState());

    //RUNNABLE:准备运行(有可能还未获得cpu资源)
    thread.start();
    log.info("{},的状态:{}", thread.getName(), thread.getState());

    Thread.sleep(10000L);
    // TERMINATED 线程远行结束后状态
    log.info("{},的状态:{}", thread.getName(), thread.getState());
}
           
JAVA线程浅剖析
runnable->blocked
/**
 * 线程状态转换测试
 *     runable-blocked
 */
@Test
public void testState2() throws InterruptedException {
    //定义锁对象
    Object lock = new Object();

    //定义Runnable类型的匿名内部类
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            synchronized (lock) {  //synchronized
                //todo 业务代码
                log.info("运行中{},的状态:{}", Thread.currentThread().getName(),
                        Thread.currentThread().getState());

                long time = System.currentTimeMillis();
//距离time之后的1s内运行
                while(System.currentTimeMillis()-time<2000L){ 
                }
            }
        }
    };

    //new:初始化线程后为开始状态
    Thread thread_a = new Thread(runnable, "线程...a");
    Thread thread_b = new Thread(runnable, "线程...b");
    log.info("new之后{},的状态:{}", thread_a.getName(), thread_a.getState());
    log.info("new之后{},的状态:{}", thread_b.getName(), thread_b.getState());

    //启动线程
    thread_a.start();
    thread_b.start();

    //沉睡1s后查看线程状态
    Thread.sleep(1000L);
    log.info("start之后{},的状态:{}", thread_a.getName(), thread_a.getState());
    log.info("start之后{},的状态:{}", thread_b.getName(), thread_b.getState());

    //主线程睡10s,避免主线程结束子线程还未结束,无法查看日志
    Thread.sleep(10000L);
    log.info("{},的状态:{}", thread_a.getName(), thread_a.getState());
    log.info("{},的状态:{}", thread_b.getName(), thread_b.getState());
}
           
JAVA线程浅剖析
runnable->waiting/timed_waiting
/**
 * 线程状态转换测试
 * runnable->waiting/timed_waiting
 */
@Test
public void testState3() throws InterruptedException {
    //new:初始化线程后为开始状态
    Thread thread_a = new Thread(() -> {
        log.info("运行中{},的状态:{}", Thread.currentThread().getName(),
                Thread.currentThread().getState());

        //TODO 模拟业务代码
        try {
            Thread.sleep(2000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }, "线程...a");
    log.info("new之后{},的状态:{}", thread_a.getName(), thread_a.getState());

    //启动线程
    thread_a.start();

    //沉睡1s后查看线程状态
    Thread.sleep(1000L);
    log.info("start之后{},的状态:{}", thread_a.getName(), thread_a.getState());

    //主线程睡10s,避免主线程结束子线程还未结束,无法查看日志
    Thread.sleep(10000L);
    log.info("{},的状态:{}", thread_a.getName(), thread_a.getState());
}
           
JAVA线程浅剖析
调整sleep(…),为无时间的wait()或者join()
//TODO 模拟业务代码
try {
    synchronized (this){
      this.wait();
    }
} catch (Exception e) {
    e.printStackTrace();
}
           
JAVA线程浅剖析
interrupt()、interrupted()、isInterrupted()
  1. interrupt()是给线程设置中断标志
  2. interrupted()是检测中断并清除中断状态(针对当前线程,Thread.interrupted()方法只针对当前线程)
  3. isInterrupted()只检测中断

线程数据结构

接下来我们从线程Thread的构造函数、类中属性、核心方法等方面分析Thread类

public
class Thread implements Runnable
           
构造函数
JAVA线程浅剖析
类中核心属性
  • MIN_PRIORITY = 1     线程最小优先级
  • State {NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED; } 枚举JVM线程6中状态
  • target   实现Runnbale接口类。执行run()方法时target非空执行target的run()方法
  • daemon 守护线程表示,默认false(守护线程在所有线程结束后自动结束,类似Time定时任务)
  • threadStatus  线程状态
  • tid  线程的ID,默认++threadSeqNumber
  • threadSeqNumber 线程的tid初始化参数
  • name 线程名字,默认在同步代码块” Thread-”+ threadInitNumber++
  • threadInitNumber 线程名字初始话使用参数
  • MAX_PRIORITY = 10   线程最大优先级
  • NORM_PRIORITY = 5   默认分配优先级

核心方法

       我们分析下创建线程并启动的所涉及的核心方法(以带有runnble参数的构造函数为例)

       创建含有业务代码的类实现Runnable接口,业务方法写到run()方法中;runnable接口仅有一个抽象方法: public abstract void run();

//实现runnable接口
class MyRunnable implements Runnable {
    int num = 0;
    @Override
    public void run() {
        log.info("我正在执行核心业务...");
    }
}
           
@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
           
创建线程并启动线程
/**
 * Thread创建和启动测试
 */
public void testThread(){
    //创建实现Runnble接口业务类
    MyRunnable myRunnable = new MyRunnable();

    //创建线程状态为new
    Thread thread = new Thread(myRunnable);

    //执行线程
    thread.start();
}
           

1) new Thread(myRunnable);

入参为target,线程名称系统默认生成(例:Thread-0,Thread-1,…)

JAVA线程浅剖析

init(…)继续调用底层的统一的init(…)方法

JAVA线程浅剖析

真正的初始化方法如下(省略部分代码)↓↓↓

JAVA线程浅剖析
JAVA线程浅剖析

thread.start();

线程启动后状态从NEW--->RUNNABLE等待CPU资源

JAVA线程浅剖析

线程获得CPU资源后执行run()方法;如果Runnable类型的target非空,执行target类中run方法

JAVA线程浅剖析

备注

方法 CPU资源 释放锁 备注
yield() 让出cup资源 NO 不释放锁
sleep(…) 让出cup资源 NO
join(…) 让出cup资源 yes 让出cup让其它线程执行
wait(…),notiyf(…) 让出cup资源 yes wait后不能自动唤醒,等待其它线程调用notify()或者notifyAll()
suspend(…),resume(…) 让出cup资源 NO 废弃的方法
I/0 让出cup资源 NO I/0效率比cup低很多
LockSupport.park() 让出cup资源 NO 底层二元信号量
       以上数据结果分析中为无返回值的形式;针对有返回值的形式,线程创建原理如上,只不过target为类FutureTask,继承接口RunnbleFuture,由于RunnableFuture接口又继承Runnble、Future接口,所以FutureTask其实也是Runnble的实现类。……详细待下回分析