文章目录
0. 线程(Thread)介绍
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。多进程和多线程都是用于实现多任务处理。
然而,我们偏向于使用多线程而不是多进程,因为线程使用共享的内存区域,不必单独分配内存区域,因此更节省内存。并且线程之间的上下文切换(Context-Switch)也比进程更节省时间。
Context Switch
: 是CPU从一个进程或线程切换到另一个进程或线程的过程。
0.1 使用多线程的优势
- 使用多线程不会阻塞用户操作,因为线程都是独立的,使得可以同时执行多个操作。
- 使用多线程更节省时间,因为可以同时执行多个操作
- 线程是独立运行的,因此一个线程如果发生异常不会影响其它线程。
0.2 多任务处理
多任务处理就是指同时执行多个任务。多任务处理可以通过两种方式实现:多进程 和 多线程。
- 多进程:
- 每个进程都有自己单独的内存区域
- 进程是重量级的
- 进程之间的通信开销比较高
- 进程之间的切换比较耗时
- 多线程
- 线程之间共享内存空间
- 线程是轻量级的
- 线程之间的通信开销比较低
0.3 进程和线程关系图
如图所示:线程在进程内执行,一个操作系统中可以有多个进程,一个进程可以有多个线程。
1. 创建线程任务
详见文章 Java创建线程任务
2. 线程的生命周期
先上一张图:
根据 Tread.State 的定义,Thread存在6种状态:
- NEW : 一个线程被初始化,但还没开始运行
- RUNNABLE : 线程正在Java虚拟机中运行,但它可能正在等待操作系统的其它资源,比如处理器。当调用了线程的start()方法之后,线程就处于RUNNABLE状态,并把线程的控制交给线程调度器。线程调度器(Thread Scheduler)可能会立马执行该线程或者将它放到线程池中等候执行。
- BLOCKED : 线程被阻塞,正在等待监视器锁
- WAITING : 线程由于调用了以下方法之一而处于WAITING状态
Object.wait
with no timeoutThread.join
with no timeoutLockSupport.park
处于WAITING状态的线程正在等待其它线程执行特定的操作。比如:一个对某个对象调用了Object.wait()的线程正在等待其它线程对该对象调用Object.notify()或者Object.notifyAll()。 一个调用了Thread.join()的线程正在等待另一个指定线程终止。
- TIMED_WAITING : 线程由于调用了以下方法之一而处于TIMED_WAITING状态
Thread.sleep
Object.wait
with timeoutThread.join
with timeoutLockSupport.parkNanos
LockSupport.parkUntil
- TERMINATED : 已经终止的线程,这个线程已经执行完成。
3. 线程调度(Thread Scheduling)
单个处理器在同一时刻只能执行一个线程。单个处理器运行多个线程时,会按照一定的顺序来执行这些线程,这个排序的过程称作线程调度。
Java运行时环境支持一种简单的确定性调度算法,称为固定优先级调度(fixed-priority scheduling),这个算法依据相对于其它线程的优先级来调度线程。
当一个线程被创建,它会继承创建它的线程的优先级。也可以通过Thread.setPriority(int priority)
方法来设置线程的优先级。线程的默认优先级是 Thread.NORM_PRIORITY
值为5,最小优先级为 Thread.MIN_PRIORITY
值为1,最大优先级为 Thread.MAX_PRIORITY
值为10。给线程设置优先级时,值范围应该在1到10之间。
3.1 优先调度(preemptive scheduling)
当有多个线程准备执行的时候,运行时系统会选择优先级最高的线程来执行,除非那个线程停止,让步(yield),或者变成了非RUNNABLE状态。如果有两个优先级相同的线程在等待CPU,调度器会任意选择一个来执行。被选择的线程会开始执行直到以下状况出现:
- 一个优先级更高的线程变成了RUNNABLE状态
- 该线程让步了,或者它的run方法退出了
- 在支持时间片(time-slicing)的系统上,时间分配已经过期了。
Java运行时系统的线程调度算法遵循优先原则,在任意时刻,当一个拥有更高优先级的线程变成RUNNABLE状态,运行时系统会选择这个新的更高优先级的线程来执行,这个新线程可以说是抢先于其它线程的。
通常,正在运行的线程优先级总是最高,但这并不是绝对的,线程调度器可能会选择有较低优先级的线程来运行以避免饥饿。因此,设置线程优先级只能影响调度策略,并不能确保算法正确性。
3.2 时间片(Time-Slicing)
根据优先调度原则,如果多个线程在一个CPU上运行,可能会出现优先级最高的线程独占CUP,导致其它线程长时间等待的情况。为了解决这一问题,某些系统允许每个线程交替在CPU上运行一小段时间,造成一种所有线程都在同时运行的印象,这种线程调度方法称为时间片。
在支持时间片的系统中,CPU给每个线程任务分配一些时间片段(非常短的一段时间,通常是毫秒级的),任务在它持有的时间段中占用CPU运行,运行完这段时间之后,退回任务池,然后把CPU让给持有下一个时间片段的任务,如此往复直到线程运行结束或者更高优先级的线程抢占CPU。线程获取到的时间片的时间长短,排列顺序以及片段数量都是不确定的。
Java平台并没有实现时间片,而某些平台可能支持时间片。因此程序不应该依赖于时间片,否则在不同系统上运行的结果可能会不一样。
4. Thread类方法介绍
详见文章 Thread类方法介绍
5. 线程池(Thread Pool)
详见文章 Java线程池
6. 线程同步
详见文章 Java线程同步