Java多线程机制
一个正在运行的程序对于操作系统而言称为进程。
程序和进程的关系可以理解为,程序是一段静态的代码,是应用程序执行的蓝本,而进程是指一个正在内存中运行的程序,并且有独立的地址空间。
线程和进程一样拥有独立的执行路径,二者的区别在于,线程存在于进程中,拥有独立的执行堆栈和程序计数器,但没有独立的存储空间。一个线程会和所属进程中的其他线程共享存储空间。
传统的程序,一个进程里只有一个线程,所以也称为单线程程序,而多线程程序是一个进程里拥有多个线程,两者间的结构区别如下图所示:
进程和线程:
进程:程序的一次动态执行
特点:系统程序运行的基本单位,进程拥有自己独立的内存空间以及一组系统资源,每个进程的内部数据和状态完全独立。
线程:程序中执行的最小单位,可以独立的完成一个顺序的流程控制,每个程序最少包含一个线程(主线程)作为程序的入口点。如果一个进程中同时运行了多个线程来完成某个程序,称为多线程。
宏观并行,微观串行
声明线程:
1.继承THread类 重写run();
2.实现Runnable接口
在操作系统中,使用进程是为了使多个程序能并发执行,以提高资源的利用率和系统吞吐量。在操作系统中再引入线程,则是为了减少系统开销,使计算机操作系统具有更好的并发性。
操作系统在操作进程时,付出的系统开销是比较大的。例如创建进程,系统在创建一个进程时,必须为它分配其所必需的资源(CPU 资源除外),如内存空间、I/O 设备以及建立相应的进程控制块。再如撤销进程,系统在撤销进程时又必须先对其所占用的资源执行回收操作,然后撤销进程控制块。如果要进行进程间切换,就要保留当前进程的进程控制块环境和设置新选中的进程的 CPU 环境。
也就是说,由于进程是一个资源的拥有者,因而在创建、撤销和切换中,系统必须为之付出较大的系统开销。所以,系统中的进程数目不宜过多,进程切换的频率也不宜过高,这也就限制了系统并发性的进一步提高。
进程是资源分配的基本单位,所有与该进程有关的资源,如打印机,输入的缓冲队列等都被记录在进程控制块中,以表示该进程拥有这些资源或正在使用它们。与进程相对应,线程是进程内一个相对独立的、可调度的执行单元。线程属于某一个进程,并与进程内的其他线程一起共享进程的资源。
线程是操作系统中的基本调度单元,所以每个进程在创建时,至少需要同时为该进程创建一个线程,线程也可以创建其他线程。
线程的三大优势
系统开销小
创建和撤销线程的系统开销,以及多个线程之间的切换,都比使用进程进行相同操作要小的多。
方便通信和资源共享
如果是在进程之间通信,往往要求系统内核的参与,以提供通信机制和保护机制。而线程间通信在同一进程的地址空间内,共享主存和文件,操作简单,无须系统内核参与。
简化程序结构
用户在实现多任务的程序时,采用多线程机制实现,程序结构清晰,独立性强。
线程的三个状态
线程是相对独立的、可调度的执行单元,因此在线程的运行过程中,会分别处于不同的状态。通常而言,线程主要有下列几种状态。
- 就绪状态:线程已经具备运行的条件,等待调度程序分配 CPU 资源给这个线程运行。
- 运行状态:调度程序分配 CPU 资源给该线程,该线程正在执行。
- 阻塞状态:线程正等待除了 CPU 资源以外的某个条件符合或某个事件发生。
下图是线程的状态转换图,通过该图,会给大家介绍线程的执行过程和状态转换。
五种操作
对线程的基本操作主要有以下五种,通过这五种操作,使线程在各个状态之间转换。
- 派生:线程属于进程的一部分,因此可以由进程派生出线程,但也可以由线程自身派生。在 Java 中,可以创建一个线程并通过调用该线程的 start()方法使该线程进入就绪状态。
- 调度:调度程序分配 CPU 资源给就绪状态的线程,使线程获得 CPU 资源进行运行,即执行 Java 线程类中
run()
方法里的内容。 - 阻塞:当线程缺少除了 CPU 资源以外的某个条件符合或某个事件时,就会进入阻塞状态。阻塞时,寄存器上下文、程序计数器以及堆栈指针都会得到保存。
- 激活:在阻塞状态下的线程,如果需要等待的条件符合或事件发生,则该线程被激活并进入就绪状态。
- 结束:在运行状态的线程,线程执行结束,它的寄存器上下文以及堆栈内容等将被释放。
守护线程
守护线程是为其他线程的运行提供便利的线程。Java 的垃圾收集机制的某些实现就使用了守护线程。
程序可以包含守护线程和非守护线程,当程序只有守护线程时,该程序才能真正结束运行。
如果要使一个线程成为守护线程,则必须在调用它的 start()
方法之前,调用线程的 setDaemon(true)
方法。并且可以使用 isDaemon()
方法的返回值( true 或 false )判断一个线程是否为守护线程。