写点什么

并发编程系列:并发编程基础

发布于: 2021 年 02 月 06 日
并发编程系列:并发编程基础

一 线程

Java 内置对多线程的支持。线程作为系统调度的最小单元,可以多个同时执行。尤其是在多核 CPU 的机器上,将会显著提升性能。

但从前面的系列文章中,我们了解到了多 cpu 处理器的工作结构(内存、总线、多级缓存、CPU),所以也清楚,当多线程使用不当时,会带来的各种不一致甚至更严重的问题。以下将从 Java 并发编程的基础开始,介绍从启动一个线程到线程池的合理使用。

二 Main 线程

作为程序的入口方法,从 main()开始执行。以下就是一个简单的示例程序:

package com.flaming.learn.jdk.thread;
import java.lang.management.ManagementFactory;import java.lang.management.ThreadInfo;import java.lang.management.ThreadMXBean;
public class MultiThread {
public static void main(String[] args){ ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false,false);
for(ThreadInfo threadInfo:threadInfos){ System.out.println("["+threadInfo.getThreadId()+"]"+threadInfo.getThreadName()); } }}
复制代码

看起来似乎应该只是一个线程,但实际执行后,在控制台可以看到如下的内容【各版本之间可能存在一点差异】:

[5]Monitor Ctrl-Break[4]Signal Dispatcher[3]Finalizer[2]Reference Handler[1]main
复制代码

这说明 Java 天然就是多线程执行。

三 为什么要使用这么多线程?

主要有以下几个原因:

3.1 应对更多的处理器核心

处理器从单核心到多核心,而且核心数量越来越多,加上超线程技术的广泛使用,使得如何更好地利用多核成为主要问题。

而线程作为大多数操作系统的基本调度单元,一个程序作为一个进程执行,可以在运行期创建多个线程,而一个线程在一个时刻只能运行在一个处理器核心上。所以多线程把计算逻辑分散到多个处理器和欣赏,就可以大幅减少整个程序的运行时间,更加高效。

3.2 更快的响应时间

对于环节较长,可拆分的复杂逻辑业务场景,可以把对一致性要求不强的操作发给其他线程处理,也可以使用消息队列。通过这种方式来使响应用户请求的线程尽可能处理完成,降低响应时间,提高用户的体验效果。

3.3 更好的编程模型

Java 通过提供良好的一致性编程模型,使我们开发时能更专注于解决问题,即选择、建立合适的模型,而无需自己从头开始考虑如何把问题多线程化。

四 线程优先级

CPU 调度大多采用时间片轮转的调度方式。操作系统把时间划分为一个个时间片,线程分配到若干时间片上,线程的时间片用完后就发生线程调度,等待下次分配。而线程优先级就是负责决定线程需要多或少分配处理器资源的线程数形。

Java 线程中的优先级控制,是通过成员变量 priority 来控制的,优先级从 1 到 10.

优先级的范围:

/**     * The minimum priority that a thread can have.     */    public static final int MIN_PRIORITY = 1;
/** * The default priority that is assigned to a thread. */ public static final int NORM_PRIORITY = 5;
/** * The maximum priority that a thread can have. */ public static final int MAX_PRIORITY = 10;
复制代码

可见默认优先级是 5,线程可以在创建时设置/修改优先级。原则:频繁阻塞(休眠或者 I/O 操作)的线程需要设置较高的优先级,而偏重计算(需要较多 CPU 时间或偏运算)的线程需要设置较低的优先级,确保不会独占处理器。

但需要注意的是,在不同的 jvm 及操作系统上,线程优先级设置可能会存在差异,有些可能会忽略线程优先级的设置

五 线程状态

Java 线程共包括以下六种状态:

线程的状态流转完整过程如下图所示:

注意的点:Java 与操作系统中,对线程状态的定义有所不同。

1、Java 中把运行就绪 两个状态都成为运行状态。

2、阻塞 是线程阻塞在进入 synchronized 关键字修饰的方法或代码块(获取锁)时的状态,但阻塞在 java.concurrent 包中 Lock 接口的线程状态是等待,这是因为 concurrent 包中 Lock 接口对于阻塞的实现采用了 LockSupport 类中的相关方法。

六 守护线程

守护线程,即 Daemon 线程,是一种支持类型的线程。主要负责程序中后台调度和支持性工作。也就是说,Java 虚拟机中不存在非 Daemon 线程的时候,Java 虚拟机会退出。可以通过调用 Thread.setDaemon(true)将线程设置为 Daemon 线程。

但需要注意,Daemon 属性设置需要在线程启动之前。在启动后设置无效。另外,Daemon 县城中如果存在 finally 代码块,那么这块代码在 Java 虚拟机退出时不一定会执行。所以,我们在使用 Daemon 线程时,不能依靠 finally 来确保执行关闭或清理资源的逻辑,因为守护线程结束后虚拟机就立即退出了。

发布于: 2021 年 02 月 06 日阅读数: 32
用户头像

磨炼中成长,痛苦中前行 2017.10.22 加入

微信公众号【程序员架构进阶】。多年项目实践,架构设计经验。曲折中向前,分享经验和教训

评论

发布
暂无评论
并发编程系列:并发编程基础