创建线程的五种方式

  • A+
所属分类:Java 多线程

本文脉络

创建线程的五种方式

创建线程的五种方式

什么是进程

进程是系统进行资源分配的基本单位。

某个程序在运行时,操作系统需要将一些系统资源分配给其使用,比如内存,CPU等。操作系统按照进程的维度来进行资源的分配。

程序不等于进程。一个程序可能开启多个进程。如登录了多个QQ账户等。

什么是线程

线程是系统进行资源调度的基本单位。

一个进程中通常包含多个线程。每个线程即是一条程序执行的具体线路。多个线程中,必须包含一个主线程。进程启动时,会从主线程开始执行,主线程调起其它子线程。子线程也可以被另一个子线程调起。主线程结束意味着进程结束,其它子线程也会结束,即使任务未完成。

线程与CPU

创建线程的五种方式

CPU中通常包含多个核,每个核主要由三部分组成,即控制器,运算器和寄存器。寄存器用于存放指令和数据,运算器用于执行指令,控制器则对运算器和寄存器进行调度。

CPU的线程切换是如下进行的:

    1. 控制器将A线程的指令及数据装入寄存器,运算器执行A线程相关的计算。

    2. 当要切换至B线程时,控制器将寄存器中原A线程的数据放入缓存,重新装入B线程的指令及数据,运算器执行B线程。

在上述过程中,运算器仅仅只是针对寄存器中的指令或数据进行运算,它并不关心这些数据来源于哪个线程。线程的控制主要基于控制器。

单位时间里,一个核只能运行一个线程。当要运行的线程数,超过CPU核心数时,就会发生线程的切换或等待。

线程并不总是在使用CPU。线程的底层操作只有两种,一种是计算,即需要使用CPU。另一种是IO,即读写,无论是从磁盘或者网络,都不需要CPU。

多少线程合适

线程过少,会造成任务堆积,无法充分利用CPU。

线程过多,会造成线程的频繁切换,也会造成不必要的资源浪费。

在分配资源时,还要考虑保留一部分CPU资源以应对突发状态。则可参考如下公式:

线程数 = 核数 * 百分比 * (1 + W/C)

其中,W为线程的等待时间,即执行IO的时间。C为线程的计算时间,即需要使用CPU的时间。W和C均为多个线程的一个平均时间。

此公式仅作为一个参考,用于设置初始值。后续,需要通过不断的观察或者以往的经验来进行不断的修正,以达到最佳的状态。

线程创建的几种方式

  1. 继承Thread类。

  2. 实现Runnable接口。

  3. 实现Callable接口。

  4. 创建ThreadPool。从线程池中获取。

常用的方式是ThreadPool,也推荐使用此方式。ThreadPool通过池的方式来管理线程,避免了频繁创建和销毁线程带来的开销,也控制了线程的最大数量,减少系统资源被耗尽的风险。

就其它三种方式来说,Callable接口形式可以提供一个返回值,通过 Future 可以取得该返回值。Runnable接口优于Thread继承,因为Java是单继承的,但是可以实现多个接口。

Java8以后,可以通过Lambda方式来创建线程。这种方式只是一个语法上的便利,本质仍然是接口的实现。

线程的状态

创建线程的五种方式

线程在其整个生命周期中,存在几种状态,如新建,就绪,运行,等待,阻塞,销毁等。就绪和运行又可合称为可执行。

处于就绪状态的线程已经具备可运行的条件,在队列中等待CPU的调度。

线程调用wait方法后进入等待状态,需要其它线程唤醒或经过预设的时间后自动唤醒。

线程在竞争某资源时,资源被其它线程占用而无法取得,则进入阻塞状态。

从CPU的角度来说,就绪,等待和阻塞的本质是三个线程队列,CPU通过合适的策略来调用队列中的线程。

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: