Java多线程

文章目录

        • 0. 线程(Thread)介绍
          • 0.1 使用多线程的优势
          • 0.2 多任务处理
          • 0.3 进程和线程关系图
        • 1. 创建线程任务
        • 2. 线程的生命周期
        • 3. 线程调度(Thread Scheduling)
          • 3.1 优先调度(preemptive scheduling)
          • 3.2 时间片(Time-Slicing)
        • 4. Thread类方法介绍
        • 5. 线程池(Thread Pool)
        • 6. 线程同步


0. 线程(Thread)介绍

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。多进程和多线程都是用于实现多任务处理。

然而,我们偏向于使用多线程而不是多进程,因为线程使用共享的内存区域,不必单独分配内存区域,因此更节省内存。并且线程之间的上下文切换(Context-Switch)也比进程更节省时间。

Context Switch: 是CPU从一个进程或线程切换到另一个进程或线程的过程。

0.1 使用多线程的优势
  • 使用多线程不会阻塞用户操作,因为线程都是独立的,使得可以同时执行多个操作。
  • 使用多线程更节省时间,因为可以同时执行多个操作
  • 线程是独立运行的,因此一个线程如果发生异常不会影响其它线程。
0.2 多任务处理

多任务处理就是指同时执行多个任务。多任务处理可以通过两种方式实现:多进程 和 多线程。

  • 多进程:
    1. 每个进程都有自己单独的内存区域
    2. 进程是重量级的
    3. 进程之间的通信开销比较高
    4. 进程之间的切换比较耗时
  • 多线程
    1. 线程之间共享内存空间
    2. 线程是轻量级的
    3. 线程之间的通信开销比较低
0.3 进程和线程关系图

Java多线程_第1张图片
如图所示:线程在进程内执行,一个操作系统中可以有多个进程,一个进程可以有多个线程。



1. 创建线程任务

详见文章 Java创建线程任务


2. 线程的生命周期

先上一张图:
Java多线程_第2张图片

根据 Tread.State 的定义,Thread存在6种状态:

  • NEW : 一个线程被初始化,但还没开始运行
  • RUNNABLE : 线程正在Java虚拟机中运行,但它可能正在等待操作系统的其它资源,比如处理器。当调用了线程的start()方法之后,线程就处于RUNNABLE状态,并把线程的控制交给线程调度器。线程调度器(Thread Scheduler)可能会立马执行该线程或者将它放到线程池中等候执行。
  • BLOCKED : 线程被阻塞,正在等待监视器锁
  • WAITING : 线程由于调用了以下方法之一而处于WAITING状态
    • Object.wait with no timeout
    • Thread.join with no timeout
    • LockSupport.park
      处于WAITING状态的线程正在等待其它线程执行特定的操作。比如:一个对某个对象调用了Object.wait()的线程正在等待其它线程对该对象调用Object.notify()或者Object.notifyAll()。 一个调用了Thread.join()的线程正在等待另一个指定线程终止。
  • TIMED_WAITING : 线程由于调用了以下方法之一而处于TIMED_WAITING状态
    • Thread.sleep
    • Object.wait with timeout
    • Thread.join with timeout
    • LockSupport.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线程同步


你可能感兴趣的:(Java)