Java并发篇_线程详解

2022-12-01 20:54:36 浏览数 (1)

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

一、线程的分类

我们知道计算机可以分为硬件和软件两大块,硬件是基础,软件提供实现不同功能的手段;而软件又可以分为操作系统和应用程序,操作系统专注于对硬件的交互管理并提供一个运行环境给应用程序使用,应用程序则是能实现若干功能的并且运行在操作系统环境中的软件。

同样,线程按照操作系统和应用程序两层次可以分为内核线程(Kernel-Level Thread)和用户线程(User Thread)。

  • 内核线程(Kernel-Level Thread,KLT) 就是直接由操作系统内核(Kernel,下称内核)支持的线程,这种线程由内核来完成线程切换
  • 用户线程(User Thread,UT) 从广义上来讲,一个线程只要不是内核线程,就可以认为是用户线程;而狭义上的用户线程指的是完全建立在用户空间的线程库上,系统内核不感知线程存在的实现。用户线程的建立、同步、销毁和调度完全在用户态中完成,不需要内核的帮助

二、Java线程的实现

Java创建线程的两种方式:实现Runnable接口,继承Thread类

  • 实现Runnable接口:写一个类实现Runnable接口,实现里面的run方法,用new Thread(Runnable target).start()方法来启动
    1. 增强了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的
    2. 线程体run()方法所在的类还可以从其他类继承一些有用的属性和方法,避免了由于Java的单继承特性带来的局限
    3. 有利于保持程序风格的一致性
  • 继承Thread类:写一个类继承自Thread类,然后重写里面的run方法,用start方法启动线程
    1. Java中只支持单继承,Thread子类无法再从其他类继承
    2. 编写简单,run()方法的当前对象就是线程对象,可直接操纵

三、Java的线程优先级

Java使用的线程调度方式就是抢占式调度

虽然Java线程调度是系统自动完成的,但是我们还是可以“建议”系统给某些线程多分配一点执行时间,另外的一些线程则可以少分配一点——这项操作可以通过设置线程优先级来完成。

Java语言一共设置了10个级别的线程优先级(Thread.MIN_PRIORITY至Thread.MAX_PRIORITY),在两个线程同时处于Ready状态时,优先级越高的线程越容易被系统选择执行。不过,线程优先级并不是太靠谱,原因是Java的线程是通过映射到系统的原生线程上来实现的,所以线程调度最终还是取决于操作系统,虽然现在很多操作系统都提供线程优先级的概念,但是并不见得能与Java线程的优先级一一对应,如Solaris中有2147483648(232)种优先级,但Windows中就只有7种,比Java线程优先级多的系统还好说,中间留下一点空位就可以了,但比Java线程优先级少的系统,就不得不出现几个优先级相同的情况了

下图显示了Java线程优先级与Windows线程优先级之间的对应关系,Windows平台的JDK中使用了除THREAD_PRIORITY_IDLE之外的其余6种线程优先级。

Java线程优先级

Windows线程优先级

1. Thread.MIN_PRIORITY

THREAD_PRIORITY_LOWEST

2.

THREAD_PRIORITY_LOWEST

3.

THREAD_PRIORITY_BELOW_NORMAL

4.

THREAD_PRIORITY_BELOW_NORMAL

5. Thread.NORM_PRIORITY

THREAD_PRIORITY_NORMAL

6.

THREAD_PRIORITY_ABOVE_NORMAL

7.

THREAD_PRIORITY_ABOVE_NORMAL

8.

THREAD_PRIORITY_HIGHEST

9.

THREAD_PRIORITY_HIGHEST

10. Thread.MAX_PRIORITY

THREAD_PRIORITY_CRITICAL

其实,即使设置了线程的优先级,一样无法确保这个线程一定先执行,因为它有很大的随机性。它并无法控制执行哪个线程,因为线程的执行,是抢占资源后才能执行的操作,而抢占到资源时,最多是给于线程优先级较高的线程一点机会而已,能不能抓住可是不一定的。

说到底就一句话:线程优化级较高的线程不一定先执行

四、Java线程生命周期

线程的生命周期包含5个阶段,包括:新建、就绪、运行、阻塞、销毁。

  • 新建:就是刚使用new方法,new出来的线程;
  • 就绪:就是调用的线程的start()方法后,这时候线程处于等待CPU分配资源阶段,谁先抢的CPU资源,谁开始执行;
  • 运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能;
  • 阻塞:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如sleep()、wait()之后线程就处于了阻塞状态,这个时候需要其他机制将处于阻塞状态的线程唤醒,比如调用notify或者notifyAll()方法。唤醒的线程不会立刻执行run方法,它们要再次等待CPU分配资源进入运行状态;
  • 销毁:如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束,那么线程就要被销毁,释放资源;

完整的生命周期图如下:

0 人点赞