Java线程的6种状态及切换教程

Java中线程的状态分为6种。

1. 初始(NEW) :新创建了一个线程对象,但还没有调用start()方法。

2.运行(RUNNABLE) :Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。

3. 阻塞(BLOCKED) :表示线程阻塞于锁。

4.  等待(WAITING) :进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。

5.  超时等待(TIMED_WAITING) :该状态不同于WAITING,它可以在指定的时间后自行返回。

6.终止(TERMINATED) :表示该线程已经执行完毕。

这6种状态定义在Thread类的State枚举中,可查看源码进行一一对应。

一、线程的状态图

二、状态详细说明

2.1. 初始状态(NEW)

实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态。

2.2. 就绪状态(RUNNABLE之READY)

  1. 就绪状态只是说你资格运行,调度程序( Cpu )没有挑选到你,你就永远是就绪状态。
  2. 调用线程的start()方法,此线程进入就绪状态。
  3. 当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态。
  4. 当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。
  5. 锁池里的线程拿到对象锁后,进入就绪状态。

2.3. 运行中状态(RUNNABLE之RUNNING)

线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一的一种方式。

3. 阻塞状态(BLOCKED)

阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。

4. 等待(WAITING)

处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。

5. 超时等待(TIMED_WAITING)

处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。

6. 终止状态(TERMINATED)

当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它终止了。这个线程对象也许是活的,但是它已经不是一个单独执行的线程。线程一旦终止了,就不能复生。

在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。

三、等待队列

调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) 代码段内。

与等待队列相关的步骤和图

  1. 线程1获取对象A的锁,正在使用对象A。
  2. 线程1调用对象A的wait()方法。
  3. 线程1释放对象A的锁,并马上进入等待队列。
  4. 锁池里面的对象争抢对象A的锁。
  5. 线程5获得对象A的锁,进入synchronized块,使用对象A。
  6. 线程5调用对象A的notifyAll()方法,唤醒所有线程,所有线程进入同步队列。若线程5调用对象A的notify()方法,则唤醒一个线程,不知道会唤醒谁,被唤醒的那个线程进入同步队列。
  7. notifyAll()方法所在synchronized结束,线程5释放对象A的锁。
  8. 同步队列的线程争抢对象锁,但线程1什么时候能抢到就不知道了。

四、同步队列状态

  1. 当前线程想调用对象A的同步方法时,发现对象A的锁被别的线程占有,此时当前线程进入同步队列。简言之,同步队列里面放的都是想争夺对象锁的线程。
  2. 当一个线程1被另外一个线程2唤醒时,1线程进入同步队列,去争夺对象锁。
  3. 同步队列是在同步的环境下才有的概念,一个对象对应一个同步队列。
  4. 线程等待时间到了或被notify/notifyAll唤醒后,会进入同步队列竞争锁,如果获得锁,进入RUNNABLE状态,否则进入BLOCKED状态等待获取锁。

五、几个方法的比较

  1. Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,但不释放对象锁,millis后线程自动苏醒进入就绪状态。作用:给其它线程执行机会的最佳方式。
  2. Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的CPU时间片,但不释放锁资源,由运行状态变为就绪状态,让OS再次选择线程。 作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似,只是不能由用户指定暂停多长时间。
  3. thread.join()/thread.join(long millis),当前线程里调用其它线程t的join方法,当前线程进入WAITING/TIMED_WAITING状态,当前线程不会释放已经持有的对象锁。线程t执行完毕或者millis时间到,当前线程一般情况下进入RUNNABLE状态,也有可能进入BLOCKED状态(因为join是基于wait实现的)。
  4. Object.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。
  5. Object.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。
  6. LockSupport.park()/LockSupport.parkNanos(long nanos),LockSupport.parkUntil(long deadlines), 当前线程进入WAITING/TIMED_WAITING状态。对比wait方法,不需要获得锁就可以让线程进入WAITING/TIMED_WAITING状态,需要通过LockSupport.unpark(Thread thread)唤醒。

六 创建线程的几种方式

Thread

线程运行的过程会产生很多信息,这些信息都保存在Thread类中的成员变量里面,常见的有:

a.线程的ID是唯一标识getId()

b.线程的名称:getName(),如果不设置线程名称默认为“Thread-xx”

c.线程的优先级:getPriority,线程优先级从1-10,其中数字越大表示优先级别越高,同时获得JVM调度执行的可能性越大,JDK内置了三种常见的状态:

`//最小优先级
public final static int MIN_PRIORITY = 1;

//一般优先级
public final static int NORM_PRIORITY = 5;

//最大优先级
public final static int MAX_PRIORITY = 10;`

一般不推荐设置线程的优先级,如果进行设置了非法的优先级程序就会出现IllegalArgumentException异常。

创建线程方式一:

继承Thread类。

步骤:

1,定义一个类继承Thread类。

2,覆盖Thread类中的run方法。

3,直接创建Thread的子类对象创建线程。

4,调用start方法开启线程并调用线程的任务run方法执行。

可以通过Thread的getName获取线程的名称 Thread-编号(从0开始)

主线程的名字就是main。

class Demo extends Thread
{
 /**
  *线程名称
  */
 private String name;
 Demo(String name)
 {
  //父类构造函数,改线程的名称
  super(name);
  //this.name = name;
 }
 //***run方法中定义就是线程要运行的任务代码。***
 public void run()
 {
  for(int x=0; x<10; x++)
  {
   //for(int y=-9999999; y<999999999; y++){}
   System.out.println(name+"....x="+x+".....name="+Thread.currentThread().getName());
  }
 }
}

class ThreadDemo2
{
 public static void main(String[] args)
 {
  Demo d1 = new Demo("旺财");
  Demo d2 = new Demo("xiaoqiang");
  d1.start();//开启线程,调用run方法。
  d2.start();
  System.out.println("over...."+Thread.currentThread().getName());
 }
}

创建线程方式二

当该类有自己父类的时候,通过实现Runnable接口,覆盖run方法。(常用)

步骤:

1,定义类实现Runnable接口。

2,覆盖接口中的run方法,将线程的任务代码封装到run方法中。

3,通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。

为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。

所以要在线程对象创建时就必须明确要运行的任务。

思想:将线程的任务通过Runnable接口封装成了对象。

4,调用线程对象的start方法开启线程。

实现Runnable接口的好处:

1,将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象的思想将任务封装成对象。

2,避免了java单继承的局限性。

//extends Fu
//准备扩展Demo类的功能,让其中的内容可以作为线程的任务执行。
//通过接口的形式完成。
class Demo implements Runnable{
 public void run()
 {
  show();
 }
 public void show()
 {
  for(int x=0; x<20; x++)
  {
   System.out.println(Thread.currentThread().getName()+"....."+x);
  }
 }
}

class ThreadDemo
{
 public static void main(String[] args)
 {
  Demo d = new Demo();
  Thread t1 = new Thread(d);
  Thread t2 = new Thread(d);
  t1.start();
  t2.start();

 }
}

创建线程方式三

实现Callable接口

与使用Runnable相比, Callable功能更强大些

1 相比run()方法,可以有返回值

2 方法可以抛出异常

3 支持泛型的返回值

4 需要借助FutureTask类,比如获取返回结果

Future接口

1  可以对具体Runnable、Callable任务的执行结果进行取消、查询是
否完成、获取结果等。

2  FutrueTask是Futrue接口的唯一的实现类

3   FutureTask 同时实现了Runnable, Future接口。它既可以作为 Runnable被线程执行,又可以作为Future得到Callable的返回值

//1.创建一个实现Callable的实现类
class Stu implements Callable {
 //2.实现call方法,将此线程需要执行的操作生命call()中
 @Override
 public Object call() throws Exception {
  int sum=0;
  for (int i = 1; i <=100; i++) {
   if(i % 2 == 0){
    System.out.println(i);
    sum += i;
   }
  }
  return sum;
 }
}

public class Bank {
 public static void main(String[] args) {
  //3.创建Callable接口实现类的对象
  Stu stu = new Stu();
  //4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
   FutureTask futureTask = new FutureTask(stu);
  //5.FutureTask的对象作为参数传递到Thread类的构造器中创建Thread,并调用start()
   new Thread(futureTask).start();
  try {
   Object sum = futureTask.get();
   System.out.println("总和为"+sum);
  } catch (InterruptedException e) {
   e.printStackTrace();
  } catch (ExecutionException e) {
   e.printStackTrace();
  }

 }
}

创建线程方式四

使用线程池

背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程, 对性能影响很大。

思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完 放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交 通工具。

好处:

1提高响应速度(减少了创建新线程的时间);

2降低资源消耗(重复利用线程池中线程,不需要每次都创建);

3便于线程管理;

corePoolSize:核心池的大小

maximumPoolSize:最大线程数

keepAliveTime:线程没有任务时最多保持多长时间后会终止

//创建并使用多线程的第四种方法:使用线程池
class MyThread implements Runnable {

  @Override
  public void run() {
    for (int i = 1; i <= 100; i++) {
      if(i % 2 ==0){
        System.out.println(Thread.currentThread().getName() + ":" + i);
      }
    }
  }

}

public class ThreadPool {
  public static void main(String[] args) {
    // 1.提供指定线程的数量
    ExecutorService service = Executors.newFixedThreadPool(10);
    //设置线程的属性
    ThreadPoolExecutor service1= (ThreadPoolExecutor) service;
    //service1.setMaximumPoolSize(15);
    //service1.setCorePoolSize();*/
    // 2.将Runnable实现类的对象作为形参传递给ExecutorService的submit()方法中,开启线程
    // 并执行相关的run()
    service.execute(new MyThread());//适用于Runnable
    //service.submit();适用于Callable

    // 3.结束线程的使用
    service.shutdown();

  }
}

总结

到此这篇关于Java线程的6种状态及切换的文章就介绍到这了,更多相关Java线程状态及切换内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 总结Java中线程的状态及多线程的实现方式

    线程的状态 线程状态图: 说明: 线程共包括以下5种状态. 1. 新建状态(New) : 线程对象被创建后,就进入了新建状态.例如,Thread thread = new Thread(). 2. 就绪状态(Runnable): 也被称为"可执行状态".线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程.例如,thread.start().处于就绪状态的线程,随时可能被CPU调度执行. 3. 运行状态(Running) : 线程获取CPU权限进行执行.需要注意

  • Java实现监控多个线程状态的简单实例

    实例如下: import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Executors; /** * 测试监控类 * * @author * */ public class WatchThread { /** * 测试函数 * * @throws InterruptedException */ public void testThre

  • Java 实现多线程切换等待唤醒交替打印奇偶数

    引言 在日常工作生活中,可能会有用时几个人或是很多人干同一件事,在java编程中,同样也会出现类似的情况,多个线程干同样一个活儿,比如火车站买票系统不能多个人买一到的是同一张票,当某个窗口(线程)在卖某一张票的时候,别的窗口(线程)不允许再卖此张票了,在此过程中涉及到一个锁和资源等待的问题,如何合理正确的让线程与线程在干同一件事的过程中,不会抢资源以及一个一直等待一个一直干活的状况,接下来就聊一下多线程的等待唤醒以及切换的过程,在此就以A和B两个线程交替打印奇偶数的例子为例,代码如下: pack

  • Java实现多线程的上下文切换

    对于上下文切换不同的操作系统模式也不尽相同,这里我们只讨论Unix系统,在我之前的文章中提到过windows的抢占式,这里就不在赘述. 无论是单核还是多核CPU都是支持多线程代码的,CPU通过给每个线程分配CPU时间片来实这个机制. 时间片是CPU分配给各个线程的时间,因为时间片非常短,所以CPU通过不停地切换线程执行,让我们感觉多个线程是同时执行的,时间片一般是几十毫秒(ms) CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务.但是,在切换前会保存上一个任务的

  • 基于java 线程的几种状态(详解)

    线程可以有六种状态: 1.New(新创建) 2.Runnable(可运行)(运行) 3.Blocked(被阻塞) 4.Waiting(等待) 5.Timed waiting(计时等待) 6.Terminated(被终止) 新创建线程: 当用new操作符创建一个新线程时,如new Thread(r),该线程还没有开始运行,它的当前状态为new,在线程运行之前还有一些基础工作要做. 可运行线程: 一旦线程调用start方法,线程处于runnable状态.在这个状态下的线程可能正在运行也可能没有运行(

  • Java线程池运行状态监控实现解析

    在实际开发过程中,在线程池使用过程中可能会遇到各方面的故障,如线程池阻塞,无法提交新任务等. 如果你想监控某一个线程池的执行状态,线程池执行类 ThreadPoolExecutor 也给出了相关的 API, 能实时获取线程池的当前活动线程数.正在排队中的线程数.已经执行完成的线程数.总线程数等. 总线程数 = 排队线程数 + 活动线程数 + 执行完成的线程数. 线程池使用示例: private static ExecutorService es = new ThreadPoolExecutor(

  • Java线程状态及切换、关闭线程的正确姿势分享

    前言 在讲线程之前有必要讨论一下进程的定义:进程是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位.进程实体由程序段, 数据段 PCB(进程控制块)组成.线程又是什么?线程可以看做轻量级进程,线程是进程的执行单元,是进程调度的基本单位 本文将详细介绍关于Java线程状态及切换.关闭线程的相关内容,下面话不多说了,来一起看看详细的介绍吧 1.线程状态及切换 Java中的线程有六种状态,使用线程Thread内的枚举类来实现,如下,我对每个状态都进行了一定的解释. public

  • 学习Java多线程之线程定义、状态和属性

    一 .线程和进程 1. 什么是线程和进程的区别: 线程是指程序在执行过程中,能够执行程序代码的一个执行单元.在java语言中,线程有四种状态:运行 .就绪.挂起和结束. 进程是指一段正在执行的程序.而线程有事也被成为轻量级的进程,他得程序执行的最小单元,一个进程可以拥有多个线程,各个线程之间共享程序的内功空间(代码段.数据段和堆空间)及一些进程级的资源(例如打开的文件),但是各个线程都拥有自己的棧空间. 2. 为何要使用多进程 在操作系统级别上来看主要有以下几个方面: - 使用多线程可以减少程序

  • RxJava的消息发送和线程切换实现原理

    RxJava是一个在Java虚拟机上的响应式扩展,通过使用可观察的序列将异步和基于事件的程序组合起来的一个库. 它扩展了观察者模式来支持数据/事件序列,并且添加了操作符,这些操作符允许你声明性地组合序列,同时抽象出要关注的问题:比如低级线程.同步.线程安全和并发数据结构等. RxJava相信大家都非常了解吧,今天分享一下RxJava的消息发送和线程源码的分析.最后并分享一个相关demo,让大家更加熟悉我们天天都在用的框架. 消息订阅发送 首先让我们看看消息订阅发送最基本的代码组成: Observ

  • Java线程的6种状态及切换教程

    Java中线程的状态分为6种. 1. 初始(NEW) :新创建了一个线程对象,但还没有调用start()方法. 2.运行(RUNNABLE) :Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为"运行".线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法.该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready).就绪状态的线程在获得CPU时间片后变为运行中状态(running). 3.

  • 一文详解Java线程的6种状态与生命周期

    目录 1.线程状态(生命周期) 2.操作线程状态 2.1.新创建状态(NEW) 2.2.可运行状态(RUNNABLE) 2.3.被阻塞状态(BLOCKED) 2.4.等待唤醒状态(WAITING) 2.5.计时等待状态(TIMED_WAITING) 2.6.终止(TERMINATED) 3.查看线程的6种状态 1.线程状态(生命周期) 一个线程在给定的时间点只能处于一种状态. 线程可以有如下6 种状态: New (新创建):未启动的线程: Runnable (可运行):可运行的线程,需要等待操作

  • Java线程的五种状态介绍

    目录 1. 线程的5种状态 2. Java线程的6种状态 3. Java线程状态的转换 1. 线程的5种状态 从操作系统层面上,任何线程一般都具有五种状态,即创建.就绪.运行.阻塞.终止. (1) 新建状态(NEW) 在程序中用构造方法创建一个新线程时,如new Thread(),该线程就是创建状态,此时它已经有了相应的内存空间和其它资源,但是还没有开始执行. (2) 就绪状态(READ) 新建线程对象后,调用该线程的start()方法就可以启动线程.当线程启动时,线程就进入就绪状态(runna

  • Java 解析线程的几种状态详解

    目录 1.线程的5种状态 2.Java线程的6种状态 3.Java线程状态的转换 总结 1. 线程的5种状态 从操作系统层面上,任何线程一般都具有五种状态,即创建.就绪.运行.阻塞.终止. (1) 新建状态(NEW) 在程序中用构造方法创建一个新线程时,如new Thread(),该线程就是创建状态,此时它已经有了相应的内存空间和其它资源,但是还没有开始执行. (2) 就绪状态(READ) 新建线程对象后,调用该线程的start()方法就可以启动线程.当线程启动时,线程就进入就绪状态(runna

  • Java线程的三种创建方式

    目录 1.Thread 2.Runnable和Thread 3.Runnable和Thread 4.三者对比 5.注意项 1.Thread 继承Thread类,并重写run方法 class ThreadDemo1 extends Thread { @Override public void run() { log.info("{}", Thread.currentThread().getName()); } } 线程启动方式: ThreadDemo1 t1 = new ThreadDe

  • Java线程(Thread)四种停止方式代码实例

    1.正常的程序启动,停止 2.使用退出标记,一般程序在run()方法后,线程会正常结束. 但是有一些伺服线程还在运行,他们运行时间较长,只有当外部条件满足时,他们才会停止.实现如下: public class ThreadCease extends Thread{ private volatile boolean exit = false; public void run(){ while(!exit){ //停止后做的事情~ } } 3.使用线程(Thread)自带的isInterrupted

  • Java线程池的简单使用方法实例教程

    目录 线程池使用场景? Java线程池使用 总结 线程池使用场景? java中经常需要用到多线程来处理一些业务,我们非常不建议单纯使用继承Thread或者实现Runnable接口的方式来创建线程,那样势必有创建及销毁线程耗费资源.线程上下文切换问题.同时创建过多的线程也可能引发资源耗尽的风险,这个时候引入线程池比较合理,方便线程任务的管理.java中涉及到线程池的相关类均在jdk1.5开始的java.util.concurrent包中,涉及到的几个核心类及接口包括:Executor.Execut

  • 简单谈谈Java 中的线程的几种状态

    Java 中的线程有以下状态: 新建状态(New):新创建的线程,还未执行. 就绪状态(Runnable):执行了 start() 方法,等待运行, 运行状态(Running):就绪状态的线程开始执行程序代码. 阻塞状态(Blocked) 同步堵塞:在运行过程中,需要拿到锁才能运行,而锁被其他资源占用,需要等待. 等待堵塞:执行了 wait() 方法,进入了等待. 其他堵塞:执行了 join().sleep() 方法,进入了等待. 终止状态(Terminated):运行完 run() 方法后结束

  • java 线程池状态及状态转换

    目录 线程池状态转移 terminated方法 总结 前言: 在 Java 中,线程池的状态和线程的状态是完全不同的, 线程有 6 种状态: NEW:初始化状态. RUNNABLE:可运行/运行状态. BLOCKED:阻塞状态. WAITING:无时限等待状态 TIMED_WAITING:有时限等待状态和 TERMINATED:终止状态. 而线程池的状态有以下 5 种: RUNNING:运行状态,线程池创建好之后就会进入此状态,如果不手动调用关闭方法,那么线程池在整个程序运行期间都是此状态. S

随机推荐