Java学习之==>Java线程生命周期与状态切换

一、Java线程生命周期与状态切换

这些状态的描述可以总结成下图:

NEW

一个刚创建但尚未启动的Java线程实例就是处于 NEW 状态

public class App {

  public static void main(String[] args) {

    Thread thread = new Thread();

    Thread.State state = thread.getState();

    System.out.println(state);
  }
}

// 输出结果
NEW

RUNNABLE

当Java线程实例调用了 Thread.start() 方法后,就会进入 RUNNABLE 状态,RUNNABLE状态包含两个子状态:READY 和 RUNNING。

  • READY:该状态的线程可以被线程调度器进行调度使之更变为RUNNING状态。
  • RUNNING:该状态表示线程正在运行,线程对象的run()方法中的代码所对应的的指令正在被CPU执行。

当Java线程实例Thread.yield()方法被调用时或者由于线程调度器的调度,线程实例的状态有可能由RUNNING转变为READY,但是从线程状态Thread.getState()获取到的状态依然是RUNNABLE。例如:

public class App {

  public static void main(String[] args) {

    Thread thread = new Thread(()->{

      Thread.yield();

      System.out.println("testThreadState");
    });

    thread.start();

    Thread.State state = thread.getState();

    System.out.println(state);
  }
}

// 输出结果
RUNNABLE
testThreadState

BLOCKED

阻塞状态,该状态下的线程不会被CPU分配执行时间,线程的状态为 BLOCKED的时候有两种可能的情况:

  • 线程正在等待一个监视器锁,只有获取监视器锁之后才能进入synchronized代码块或者synchronized方法,在此等待获取锁的过程线程都处于阻塞状态。
  • 线程X已步入synchronized代码块或者synchronized方法后(此时已经释放监视器锁)调用Object.wait()方法之后进行阻塞,当接收其他线程T调用该锁对象Object#notify()/notifyAll(),但是线程T尚未退出它所在的synchronized代码块或者synchronized方法,那么线程X依然处于阻塞状态(注意API注释中的reenter,理解它场景2就豁然开朗)。

public class App {

  public static void main(String[] args) {
    App app = new App();

    Thread thread1 = new Thread(() -> {
      app.produce();
    });

    Thread thread2 = new Thread(() -> {
      app.consumer();
    });

    thread1.start();
    thread2.start();
    System.out.println("thread1:" + thread1.getState());
    System.out.println("thread2:" + thread2.getState());
  }

  public void produce() {
    synchronized (this) {
      try {
        Thread.sleep(5000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  public void consumer() {
    synchronized (this) {
      System.out.println("testThread");
    }
  }
}

// 输出结果
thread1:RUNNABLE
thread2:BLOCKED
testThread

第一种情况

thread1先运行,拿到 synchronized 锁,执行 produce方法中的 synchronized 代码块中的代码-->等待5秒,这时候 thread2 再运行,状态为 BLOCKED,等到 thread1执行 synchronized代码块结束,thread2 拿到 synchronized 锁,才能执行 consume 方法中的 synchronized 代码块中的代码。

public class App {

  public static void main(String[] args) throws InterruptedException {
    App app = new App();

    Thread thread1 = new Thread(() -> {
      app.produce();
    });

    Thread thread2 = new Thread(() -> {
      app.consumer();
    });

    thread1.start();
    thread2.start();
    // 这里让主线程sleep 1500毫秒从而让thread2调用了notify()并且尚未退出synchronized代码块,确保thread1执行了wait()
    Thread.sleep(1500);
    System.out.println("thread1:" + thread1.getState());
    System.out.println("thread2:" + thread2.getState());
  }

  public void produce() {
    synchronized (this) {
      try {
        this.wait();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  public void consumer() {
    synchronized (this) {
      try {
        this.notify();
        Thread.sleep(3000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println("testThread");
    }
  }
}

// 输出结果
thread1:BLOCKED
thread2:TIMED_WAITING
testThread

第二种情况

thread1先运行,执行 wait() 方法进入 WAITING状态并释放锁,thread2再执行,执行 notify()方法唤醒 thread1,本来 thread1应该变化为 RUNNABLE状态,但是因为 thread2的synchronized代码块还没执行完,后面还有一个 Thread.sleep(3000)没执行完,thread2还拿着这把锁,所以,thread1就还是BLOCKED阻塞状态。

WAITING

无限期的等待状态,这种状态下的线程不会被CPU分配执行时间。当一个线程执行了某些方法后就会进入 WAITING 状态,被显式唤醒后又变化为 RUNNABLE状态继续执行。

RUNNABLE 转换为 WAITING 的方法:

  • Object#wait()
  • Thread#join()
  • LockSupport.park()

WAITING 转换为 RUNNABLE 的方法:

  • Object#notify()或者Object#notifyAll()
  • LockSupport.unpark(thread)
public class App {

  public static void main(String[] args) throws InterruptedException {
    App app = new App();

    Thread thread = new Thread(() -> {
      app.produce();
    });

    thread.start();
    // 这里让主线程sleep 1500毫秒,确保thread执行了wait()
    Thread.sleep(1500);
    System.out.println("thread:" + thread.getState());
  }

  public void produce() {
    synchronized (this) {
      try {
        this.wait();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

// 输出结果
thread:WAITING

TIMED_WAITING

有限期等待状态,它和WAITING有点相似,这种状态下的线程不会被分配CPU执行时间,不过这种状态下的线程不需要被显式唤醒,只需要等待超时限期到达就会被唤醒。

RUNNABLE转换为TIMED_WAITING的方法:

  • Object#wait(timeout)
  • Thread#sleep(timeout)
  • Thread#join(timeout)
  • LockSupport.parkNanos(timeout)
  • LockSupport.parkUntil(timeout)

待超时时间结束,就由TIMED_WAITING状态转换为RUNNABLE状态。

public class App {

  public static void main(String[] args) throws InterruptedException {
    App app = new App();

    Thread thread = new Thread(() -> {
      app.produce();
    });

    thread.start();
    // 这里让主线程sleep 50毫秒,确保thread执行了Thread.sleep(3000)
    Thread.sleep(50);
    System.out.println("thread:" + thread.getState());
  }

  public void produce() {
    synchronized (this) {
      try {
        Thread.sleep(3000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

// 输出结果
thread:TIMED_WAITING

TERMINATED

TERMINATED状态表示线程已经终结。一个线程实例只能被启动一次,准确来说,只会调用一次Thread#run()方法,Thread#run()方法执行结束之后,线程状态就会更变为TERMINATED,意味着线程的生命周期已经结束。

public class App {

  public static void main(String[] args) throws InterruptedException {
    App app = new App();

    Thread thread = new Thread(() -> {
      app.produce();
    });

    thread.start();
    // 这里让主线程sleep 50毫秒,确保thread执行了synchronized代码块中的代码
    Thread.sleep(50);
    System.out.println("thread:" + thread.getState());
  }

  public void produce() {
    synchronized (this) {
      System.out.println("testThread");
    }
  }
}
// 输出结果
testThread
thread:TERMINATED

原文地址:https://www.cnblogs.com/L-Test/p/12112480.html

时间: 2024-10-12 15:21:55

Java学习之==>Java线程生命周期与状态切换的相关文章

Java多线程与并发——线程生命周期和线程池

线程生命周期:  线程池:是预先创建线程的一种技术.线程池在还没有任务到来之前,创建一定数量的线程,放入空闲队列中,然后对这些资源进行复用.减少频繁的创建和销毁对象. java里面线程池的顶级接口是Executor,是一个执行线程的工具. 线程池接口是ExecutorService. java.util.concurrent包:并发编程中很常用的实用工具类 Executor接口:执行已提交的Runnable任务的对象. ExecutorService接口:Executor提供了管理终止的方法,以

Java多线程(二)、线程的生命周期和状态控制(转)

Java多线程(二).线程的生命周期和状态控制 分类: javaSE综合知识点 2012-09-10 16:11 15937人阅读 评论(3) 收藏 举报 一.线程的生命周期 线程状态转换图: 1.新建状态 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态.处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态(runnable). 注意:不能对已经启动的线程再次调用start()方法,否则会出现java.lang.IllegalThreadSt

java基础——线程的常用方法和线程生命周期

线程的常用方法 package thread; /* 测试Thread类中的常用方法: 1.start() 2.run():重写Thread方法,将线程要执行的操作声明在方法中 3.Thread.currentThread():静态方法,返回执行当前代码的线程 4.getName():获取当前线程的名字 5.setName():设置当前线程的名字 6.yield():当前线程交出cpu执行权 7.join():在线程a中调用线程b的join方法,此时线程a进入阻塞态,直到线程b完全执行完后,a才

java学习笔记15--多线程编程基础2

本文地址:http://www.cnblogs.com/archimedes/p/java-study-note15.html,转载请注明源地址. 线程的生命周期 1.线程的生命周期 线程从产生到消亡的过程 一个线程在任何时刻都处于某种线程状态(thread state) 线程生命周期状态图 诞生状态 线程刚刚被创建 就绪状态 线程的 start 方法已被执行 线程已准备好运行 运行状态 处理机分配给了线程,线程正在运行 阻塞状态(Blocked) 在线程发出输入/输出请求且必须等待其返回 遇到

多线程(四)线程生命周期和线程池

一.线程生命周期 线程的5种状态: 新建(New) ,就绪(Runnable),运行(Running),阻塞(Blocked),死亡(Dead)     线程生命周期图: 二.线程池 1.为什么要使用线程池: (1).提高性能 系统启动一个新线程的成本是比较高的,而使用线程池避免了频繁的创建和销毁线程,可以很好地提高性能. 线程池在系统启动时即创建大量空闲的线程,程序将一个Runnable对象或Callable对象传给线程池,线程池就会自动 启动一个线程来执行它们的run()或call()方法,

线程八大基础核心四(线程生命周期)

1.引子 在java多线程并发编程中,有八大基础核心.考考你:看看都有哪八大基础核心呢?它们分别是: 1.创建线程的方式 2.线程启动 3.线程停止 4.线程生命周期 5.线程相关的方法 6.线程相关的属性 7.线程异常处理 8.线程安全 今天我们从第四个基础核心开始:线程生命周期 2.考考你 #前情回顾: 在java编程语言中,从线程创建,到线程执行结束,会经过一系列状态的转化,称为线程的生命周期 #考考你: 1.你知道线程生命周期中有哪些状态吗? 2.你知道各种状态对应的含义吗? 3.一图胜

这么理解线程生命周期,是不是很简单?

| 好看请赞,养成习惯 你有一个思想,我有一个思想,我们交换后,一个人就有两个思想 If you can NOT explain it simply, you do NOT understand it well enough 现陆续将Demo代码和技术文章整理在一起 Github实践精选 ,方便大家阅读查看,本文同样收录在此,觉得不错,还请Star?? 为什么要了解线程的生命周期? 之前写过 Spring Bean 生命周期三部曲: Spring Bean生命周期之缘起 Spring Bean生

JAVA学习篇--Java类加载

由来: 与普通程序不同的是,Java程序(class文件)并不是本地的可执行程序(解释性语言).当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Javaclass加载到JVM里头运行,负责加载Javaclass的这部分就ClassLoader.中文叫做类加载器. 类加载器就好比一个代理,你需要什么,我通过类加载器将你需要的内容返回给你! 类加载器有什么作用? 当程序需要的某个类,那么需要通过类加载器把类的二进制加载到内存中. 解释: 类加载器也是Java类,因为其他是java类的

iOS多线程全套:线程生命周期,多线程的四种解决方案,线程安全问题,GCD的使用,NSOperation的使用

目的 本文主要是分享iOS多线程的相关内容,为了更系统的讲解,将分为以下7个方面来展开描述. 多线程的基本概念 线程的状态与生命周期 多线程的四种解决方案:pthread,NSThread,GCD,NSOperation 线程安全问题 NSThread的使用 GCD的理解与使用 NSOperation的理解与使用 Demo在这里:WHMultiThreadDemo Demo的运行gif图如下: 一.多线程的基本概念 进程:可以理解成一个运行中的应用程序,是系统进行资源分配和调度的基本单位,是操作