Java线程详解

1. 线程相关概念

串行

单线程依次执行每个任务;

并行

开启多个线程多个任务同时执行;

线程

一个进程中任务执行的具体单元,任务执行的最小的单元(CPU调度的最小单元),线程之间资源共享。

进程

在JVM中,启动一个main()方法就是启动了一个进程,进程里又包含了很多线程,线程又叫轻量级进程。进程是对程序运行所占用各种资源的描述(CPU,内存),进程之间资源不共享,是资源分配的最小单元。

线程的生命周期

线程在java中也是一个对象,一个对象的生命周期是指实例化完成到对象销毁的中间过程。

线程类型
  • 内核线程(KLT)
    线程的所有管理操作由操作系统内核完成,内核通过操作调度器调度线程到内核线程表对线程进行维护,每个内核线程映射到CPU各个处理器。内核线程涉及到用户态和内核态的切换。
    JAVA线程创建是依赖于系统内核,通过JVM调用系统库创建内核线程。JVM运行在用户空间,涉及到线程操作依赖底层内核,因此会从用户态切换到内核态。

JVM线程调度流程图:
在这里插入图片描述

  • 用户线程(ULT)
    在用户程序中实现的线程,不依赖操作系统核心。线程创建,管理,调度依赖用户空间的线程库提供的函数,线程的实现系统内核不能感知到,由程序自己完成对线程的管理。

2. 线程的六种状态及状态间的转换

在JDK源码中(java.Lang.Thread),可以看到在Java中给线程定义了六种状态:

2.1 初始化(NEW):

新创建了一个线程对象,但还没有调用start()方法。Thread A = new Thread();

2. 2 运行(RUNNABLE):

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

2.3 阻塞(BLOCKED):

表示线程阻塞于锁。
BLOCKED状态下线程不会释放当前占用的系统资源,进入ready状态后等待CPU选中分配资源执行。
进入BLOCKED状态的条件:
在RUNNING状态下:
等待用户输入;
调用sleep()方法;
调用join()方法;
从BLOCKED回到就绪状态的条件:
用户输入完毕;
等待休眠时间结束;
合并进来的线程执行结束。

2.4 等待(WAITING):

进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。

2.5 超时等待(TIMED_WAITING):

该状态不同于WAITING,它可以在指定的时间后自行返回。处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。sleep(超时时间)

2.6 终止(TERMINATED):

表示该线程已经执行完毕。

  1. 当前线程执行完run()方法或者主线程的main()执行结束,我们就认这个线程终止了。这个线程对象也许是活的,但它已经不是一个单独执行的线程。线程一旦终止了,就不能复生。
  2. 在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。

线程状态转换图

在这里插入图片描述在这里插入图片描述
前面已经说了初始到运行,运行内部两种状态之间,运行到阻塞,运行到终止这几种状态之间的转换。下面详细介绍一下运行与等待之间的转换过程。
在上面第二图中可以看到,在运行状态下调用wait()方法就会进入WAITING状态,在WAITING状态下线程会经历一个复杂的过程。
进入等待队列
首先,调用wait()方法后实际进入的是等待队列。当发现当前线程需要的资源处于同步(synchronized)状态时,就需要获取对象锁才能使用对象。线程使用完对象锁之后调用wait()方法进入等待队列,处于TIMEWAITING状态;同时当前线程会释放所占有的JVM资源,进入这个状态过后是不能自动唤醒的,必须调用notify()或者notifyAll()方法,线程进入WAITING(同步队列)。

等待队列的操作过程图解
在这里插入图片描述
进入同步队列(锁池)

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

线程及状态详解参考博客

3. 线程创建的三种方式

继承Thread类重写run()方法
受单继承限制且子线程没有返回值不能抛出异常
实现Runable接口
Runable接口只有一个run()方法,重写run()方法。可以实现多个接口,开发更加灵活;但子线程没有返回值且不能抛出异常。实现了Runable接口的类要作为创建Thread类的一个参数,最终还是由Thread类实例化线程,调用start()方法。
实现Callable接口
实现Callable接口,重写call()方法,然后包装成java.util.concurrent.FutureTask, 再然后包装成Thread。有返回值,可以抛出异常

线程的执行顺序跟调用start()方法的顺序无关,是CPU随机选取执行的。

线程创建代码参考博客
线程常用方法详解参考博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值