关于多线程你了解多少?

或许是执念太重,又或许是性格缺陷,我对java中一些知识的坚持,已经到了让人无法接受的地步。有些人甚至因此在背后骂我神经病、傻瓜。但我依旧我行我素,即使中间懈怠了很长时间,重新开始时我依旧会以这些知识为起点。不过,如此这般循环往复,我对这些知识的理解却依旧模模糊糊。这着实是天底下最大的笑话!譬如本篇要梳理的多线程。为了掌握它,我不仅买了很多相关的书籍,而且还买了超级多的视频课程,但最终也就那样——面试时吞吞吐吐,语焉不详;与人讨论时仍然毫无想法,好似一张刚做出来的白纸一样。每每这时,我都极力深刻反省,但情绪化的想法总会让我认为造成这种结果的原因是那些人不愿意给予机会于我。直到最近读了了凡先生的《了凡四训》,我才有所顿悟:原来所有的结果都是自己造就的,并非别人造成的。

在本章中,我将结合以前的课程、资料以及网络上的一些资源,对java多线程进行一次自认为相对全面的梳理,以明晰这些知识的前后逻辑,加深本人对这些知识的理解。本节将从以下几个方面展开:

  • 多线程的基本知识(多线程的基本概念、java中多线程的基本操作)
  • 与多线程相关的一些知识的梳理
  • java内存模型
  • java多线程与内存模型之间的关系

1 多线程的基本知识

看到这个标题,我虽然很想多说一些,但实在不知道从何说起。这不仅仅是因为自己的表达能力有欠缺,更是因为自己的知识是碎片化的,是不完整的。不过,既然已经决定出来出丑了,所有的不足都已经不重要了,耐心做好本小节的梳理才是对自己最好的报答。

1.1 常见概念

首先让我们来看几组在多线程开发中经常遇到的概念:a) 同步与异步(这组概念通常用来形容一次方法调用所谓同步是指方法调用一旦开始,调用者就必须等被调用方法执行结束后,才能继续后面的操作所谓异步是指方法调用一旦开始,就会立即返回,调用者几乎不用等待,就可以继续后面的操作,而被调用的方法会在另一个线程中真实的执行,整个执行过程不会阻碍调用者的工作。【注意这里出现了一个名词——线程】)。下面这幅图展示了同步调用与异步调用之间的关系:

 从图中可以看出两个时间线(图中的两个向右的箭头),其中最上面的时间线上方法调用是同步进行的,也就是说后一个逻辑需要在前一个逻辑完成后才能进行下一步逻辑。而异步显然不会这样,从图中可以可看出方法调用了另一个时间线,但是该调用后立马返回,另一时间线上的处理并不会影响本时间线上的处理。b) 并发和并行(这两个概念非常容易混淆,虽然它们都可以表示两个或者多个任务一起执行,但侧重点有所不同并发侧重于多个任务交替执行,而多个任务之间有可能还是串行的,而并行是真正意义上的“同时执行”。大家都用过多核CPU,它里面的执行就可能是并行执行,然而在单核CPU中,即使有多进程或者多线程任务,那么真实环境中这些任务也不可能是并行的,因为一个CPU一次只能执行一条指令,因此在这种情况下多进程之间是并发执行的)。具体可以参见下面这幅图:

这幅图中,左侧是一个并行流程,右侧是一个并发流程。c) 临界区(临界区通常用于表示一组公共资源或共享数据,它可以被多个线程使用。不过每次,只能有一个线程使用它,一旦临界区资源被占用,其他线程要想使用这个临界区资源就必须等待)。d) 阻塞和非阻塞阻塞和非阻塞通常用来形容多线程之间的相互影响比如一个线程占用了临界区资源,那么其他所有需要这个资源的线程就必须在这个临界区中等待。等待会导致线程挂起,这种情况就是阻塞。如果一旦出现某个占用资源的线程不愿意释放资源,那么其他所有阻塞在这个临界区上的线程就不能工作【注意这里出现了另外一个名词——线程阻塞】)。e) 死锁、饥饿和活锁(这三个名词都属于多线程的活跃性问题。如果出现这几种情况,那么相关线程就可能不再活跃了,也就是说它们很难再继续执行下去。所谓死锁就是参与活动的几个线程之间相互持有彼此需要的锁,比如A线程持有A锁,但此时需要另外一个B锁,而B线程持有B锁,却同时需要另外一个A锁,那么此时A、B线程之间就出现了死锁。所谓饥饿是指某一个或多个线程因为某种原因无法得到所需的资源,导致一直无法执行。比如可能线程优先级低,导致高优先级线程一直抢占它们所需的资源,进而导致它们无法工作。所谓活锁则是一种因为彼此过于礼貌而出现的线程无法执行的情况,比如线程A需要锁C,而线程B也需要锁C,此时线程A发现有人需要就让出锁C的使用权,希望对方先执行,而线程C也发现了这个问题,也主动让出了锁C的使用权)。

1.2 Java中线程的基本概念

接着让我们来看一下java中与线程有关的基本知识:在计算机中进程是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。由此可以直到进程可以容纳若干个线程。其实线程就是一个轻量级的进程,是程序执行的最小单位。使用多线程而非用多进程去进行并发程序的设计,是因为线程间的切换和调度的成本远远小于进程。下面这张图展示了线程的生命周期:

其实这些状态都在Thread类中的State枚举类中有定义,这个枚举类的源代码如下所示,不过还是建议浏览jdk源码:

public enum State {/*** Thread state for a thread which has not yet started.*/NEW,/*** Thread state for a runnable thread.  A thread in the runnable* state is executing in the Java virtual machine but it may* be waiting for other resources from the operating system* such as processor.*/RUNNABLE,/*** Thread state for a thread blocked waiting for a monitor lock.* A thread in the blocked state is waiting for a monitor lock* to enter a synchronized block/method or* reenter a synchronized block/method after calling* {@link Object#wait() Object.wait}.*/BLOCKED,/*** Thread state for a waiting thread.* A thread is in the waiting state due to calling one of the* following methods:* <ul>*   <li>{@link Object#wait() Object.wait} with no timeout</li>*   <li>{@link #join() Thread.join} with no timeout</li>*   <li>{@link LockSupport#park() LockSupport.park}</li>* </ul>** <p>A thread in the waiting state is waiting for another thread to* perform a particular action.** For example, a thread that has called {@code Object.wait()}* on an object is waiting for another thread to call* {@code Object.notify()} or {@code Object.notifyAll()} on* that object. A thread that has called {@code Thread.join()}* is waiting for a specified thread to terminate.*/WAITING,/*** Thread state for a waiting thread with a specified waiting time.* A thread is in the timed waiting state due to calling one of* the following methods with a specified positive waiting time:* <ul>*   <li>{@link #sleep Thread.sleep}</li>*   <li>{@link Object#wait(long) Object.wait} with timeout</li>*   <li>{@link #join(long) Thread.join} with timeout</li>*   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>*   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>* </ul>*/TIMED_WAITING,/*** Thread state for a terminated thread.* The thread has completed execution.*/TERMINATED;
}

到这里,我想起了《重新认识AbstractQueuedSynchronizer》这篇文章。在梳理这篇文章时我想到了很多问题:java中的多线程是什么?java中线程的状态有哪些?java中线程的通信方式有几种?这些都是面试时,面试官问过的问题。现在来看第二个问题:java中线程的状态有6种,它们分别为:新建(NEW)、运行时(RUNNABLE)、阻塞(BLOCKED)、有限等待(TIMED_WAITING)、等待(WAITING)、终结(TERMINATED)。其中NEW表示线程刚刚创建。当调用线程的start()方法后,线程就开始执行了(注意这个说法只是为了让我们有个形象的了解),此时线程处于RUNNABLE状态(这里要清楚线程处在这个状态就表示线程所需的一切资源都已经准备好了)。如果线程在执行过程中,遇到了synchronized同步块,就会进入BLOCKED阻塞状态,这时线程处于暂停状态,直到获得请求的锁。WAITING和TIMED_WAITING都表示等待状态,区别在于前者会进入一个无时间限制的等待,后者会进入一个有时限的等待(处于等待状态的线程究竟在等什么呢?一般处于这种状态的线程是在等待一些特殊的事件。比如通过wait()方法等待的线程在等待notify()方法,而通过join()方法等待的线程则会等待目标线程的终止。注意:一旦等到了期望的事件,线程就会再次执行,进入RUNNABLE状态)。当线程执行完毕,则会进入TERMINATED状态。【注意从NEW状态出发后,线程不能再回到NEW状态,同理,处于TERMINATED状态的线程也不能再回到RUNNABLE状态】

1.3 Java中线程的基本操作

通过前面的梳理,我们知道了Java线程的几种状态,期间也有一些新的操作映入眼帘,比如join()方法,那Java中线程的基本操作到底有那些呢?记得这个问题在之前的文章中做过梳理(譬如:《ThreadLocal“你”真的了解吗?(一)》),但这些仅仅是简单的介绍,并不完善,这里我将再做一次梳理:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/410459.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu上搭建Nginx环境

1. 软件包下载 nginx下载地址 下载linux版本的nginx&#xff0c;如图圈示 2. 将下载好的软件包上传至Linux服务器 假设上传到 /opt/nginx 目录,进入目录 cd /opt/nginx解压&#xff0c;根据版本自行修改版本号 tar zxvf nginx-1.16.0.tar.gz3.安装 安装编译所需的依赖&a…

前端算法 === 力扣 111 二叉树的最小深度

目录 问题描述 DFS&#xff08;深度优先搜索&#xff09;方案 BFS&#xff08;广度优先搜索&#xff09;方案 总结 力扣&#xff08;LeetCode&#xff09;上的题目111是关于二叉树的最小深度问题。这个问题可以通过深度优先搜索&#xff08;DFS&#xff09;和广度优先搜索&…

QJson的写入和解析基本操作

一、QJson简介 QJson 是一个用于处理 JSON&#xff08;JavaScript Object Notation&#xff09;数据的 C 库 JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式 JSON 的语法简洁明了&#xff0c;使用人类可读的文本格式来表示数据 它由键值…

分块矩阵的转置

证明 则 证明&#xff1a;令&#xff0c;有&#xff0c;对它做一个分块使得和后面的分块矩阵中的是同型矩阵&#xff0c;要证明&#xff08;任意的&#xff09;&#xff0c;需要证明1&#xff09;是一个的矩阵 2&#xff09;任意的 首先证明1&#xff09;我们先定义两个函…

Getting RateLimitError while implementing openai GPT with Python

题意&#xff1a;“在使用 Python 实现 OpenAI GPT 时遇到 RateLimitError 错误。” 问题背景&#xff1a; I have started to implement openai gpt model in python. I have to send a single request in which I am getting RateLimitError. “我开始在 Python 中实现 Ope…

SSH弱口令爆破服务器

一、实验背景 1、概述 使用kali的hydra进行ssh弱口令爆破&#xff0c;获得服务器的用户名和口令&#xff0c;通过 ssh远程登录服务器。 2、实验环境 kali攻击机&#xff1a;192.168.1.107 centos服务器&#xff1a;192.168.1.105 二、前置知识 1、centos设置用户并设置弱…

HR招聘,如何解决面试流程繁琐的问题

要解决面试流程繁琐的问题&#xff0c;就必须要精简和优化招聘流程。比如精简面试环节&#xff0c;制定标准化流程&#xff0c;完善信息管理&#xff0c;对面试环节进行细致梳理之后&#xff0c;尽快识别并去除那些不必要的步骤&#xff0c;这样就能够减少求职者的等待时间&…

IAR软件配置笔记

Project->Optiions->配置Device Debug中配置 C/C Compiler中配置 优化等级 C语法标准选择 回到主界面&#xff0c;Tools->Options 字体调整 Editor更改缩进数 Project->Make编译 调试模式和编辑模式的View菜单栏不一样http://t.csdnimg.cn/JsWjy Disa…

Python | Linux | 解析Himawari-8/9 | Standard Data

写作前面 之前一个相关的工作需要解析Himawari-8/9 Standard Data文件&#xff0c;因为他是二进制的&#xff0c;之前没有处理过&#xff0c;导致完全摸不着头脑。在网上找了中英文搜索找了好久&#xff0c;虽然也找到了公开的解析代码&#xff0c;但是放在自己的数据这感觉总是…

Golang | Leetcode Golang题解之第375题猜数字大小II

题目&#xff1a; 题解&#xff1a; func getMoneyAmount(n int) int {f : make([][]int, n1)for i : range f {f[i] make([]int, n1)}for i : n - 1; i > 1; i-- {for j : i 1; j < n; j {f[i][j] j f[i][j-1]for k : i; k < j; k {cost : k max(f[i][k-1], f[…

字节跳动-生活服务-java后端-一面

基础题 计算机网络 1.tcp三次握手和四次挥手&#xff1f;tcp的第三次握手可以传输应用层数据嘛&#xff1f; 4.1 TCP 三次握手与四次挥手面试题 | 小林coding (xiaolincoding.com) 2.描述一下打开百度首页后发生的网络过程&#xff1f; 计算机网络面试题 | 小林coding (xi…

无损放大图片,盘点5款最新无损放大图片软件

我们常常遇到需要放大图片却又不希望损失画质的尴尬境地。无论是为了打印大幅海报、在线展示高清细节&#xff0c;还是想要修复珍贵的老照片&#xff0c;无损放大图片成为了许多人的迫切需求。下面给大家分享5款最新无损放大图片软件&#xff0c;高效且实用&#xff0c;一起来学…

C++基础练习

1》提示并输入一个字符串&#xff0c;统计该字符串中字母个数、数字个数、空格个数、其他字符的个数 1 #include<iostream>2 using namespace std;3 4 int main()5 {6 string str1; //定义字符串数据7 cout << "请输入一个字符串>>>" ;8…

好出创新点的方向:SAM做医学图像分割!轻松登Nature!

继MedSAM登上Nature后&#xff0c;牛津大学也最新提出了MedSAM-2&#xff0c;不但分割一切医学图像&#xff0c;还能分割视频&#xff01;准确度提升一个level&#xff0c;直接刷新医学图像分割SOTA榜&#xff01; 这种惊人的医学图像分割效果都得益于SAM模型&#xff08;尤其是…

【html+css 绚丽Loading】 000020 三才流转盘

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽Loading&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495…

AList嵌入动态验证码实现动态校验

前言 晓杰利用ALists创建了个网盘资源站&#xff0c;想着如何增加个动态验证码进行验证后才能进行访问下载&#xff0c;刚开始利用了固定的验证码&#xff0c;用户可以通过JS代码中进行绕过或直接拿到验证码&#xff0c;经过晓杰多次优化&#xff0c;最终版本支持动态获取验证…

Redis 实现哨兵模式

目录 1 哨兵模式介绍 1.1 什么是哨兵模式 1.2 sentinel中的三个定时任务 2 配置哨兵 2.1 实验环境 2.2 实现哨兵的三条参数&#xff1a; 2.3 修改配置文件 2.3.1 MASTER 2.3.2 SLAVE 2.4 将 sentinel 进行备份 2.5 开启哨兵模式 2.6 故障模拟 3 在整个架构中可能会出现的问题 …

一道关于php文件包含的CTF题

一、源码 这是index.php的页面。 点击login后会发现url里多了action的参数&#xff0c;那么我们就可以通过它来获取源码。 ?actionphp://filter/readconvert.base64-encode/resourcelogin.php 再通过base64的解码可以查看源码。 index.php源码&#xff1a; <?php erro…

可拖拽表单设计器都有哪些突出特点?

为了提高效率、降低开发成本&#xff0c;利用低代码技术平台的优势特点可以实现这一目标。究竟什么是低代码技术平台&#xff1f;都有哪些值得夸耀的特点和优势&#xff1f;今天&#xff0c;我们就带着这些问题&#xff0c;一起来了解低代码技术平台、可拖拽表单设计器的多个优…

第一周学习--联邦学习

OUC读研--第一周 目录 1、课程学习 2、fedavg的算法实现 关于代码详解 1、client __init__ 方法 local_train 方法 2、server 3、get_dataset 函数定义 数据集加载 MNIST 数据集 CIFAR-10 数据集 返回值 使用示例 4、 main 代码解释 可能的改进点 5、models …