java多线程编程_《java多线程编程实战指南》读书笔记 -- 基本概念

62221d6bc89357e78e6ce504b49581fc.png


展开
并发:多个线程操作相同资源,保证线程安全,合理使用资源
高并发:服务能同时处理多个请求,提高程序性能
测试上下文切换工具

  • Lmbench3 测量上下文切换时长
  • vmstat 测量上下文切换次数

减少上下文切换

  • 无锁并发编程:将数据ID按hash算法取模分段,不同线程处理不同段数据。
  • CAS算法
  • 使用最少线程
  • 协程:在单线程中实现多任务调度并维持任务间切换

避免死锁

  • 避免一个线程同时获取多个锁
  • 避免一个线程在锁内同时占用多个资源
  • 尝试使用定时锁,使用lock.tryLock(timeout)来代替使用内部锁机制
  • 对数据库锁,加锁和解锁必须在一个数据库连接里

创建线程成本
java平台中,线程就是一个对象,创建需要分配内存。
与普通对象不同,jvm会为每个线程分配调用栈所需的内存空间,调用栈用于跟踪java方法间的调用关系以及java代码对Native Code(多为 C代码)的调用。
java中的每个线程可能还有一个内核线程(具体与jvm实现有关)与之对应。
线程状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jBnNfHDw-1576115827051)(evernotecid://626F545F-28E7-432D-B442-A76BAC946322/appyinxiangcom/14768996/ENResource/p91)]
获取线程转储(Thread dump)的方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4EOcSMZc-1576115827052)(evernotecid://626F545F-28E7-432D-B442-A76BAC946322/appyinxiangcom/14768996/ENResource/p92)]
eg:
mac中,使用jstack获取线程转储//进入jdk安装地址 » /usr/libexec/java_home -V » cd /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home //获取当前所有引用PID /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home » jps 47697 Launcher 47698 OrtApp 46002 46535 KotlinCompileDaemon 47818 Jps //获取线程转储 /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home » jstack -l 47698 2019-09-23 09:24:48 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.181-b13 mixed mode): "lettuce-kqueueEventLoop-9-1" #223 daemon prio=5 os_prio=31 tid=0x00007fe6cd166800 nid=0x16313 runnable [0x0000700018ed0000] java.lang.Thread.State: RUNNABLE at io.netty.channel.kqueue.Native.keventWait(Native Method) at io.netty.channel.kqueue.Native.keventWait(Native.java:94) at io.netty.channel.kqueue.KQueueEventLoop.kqueueWait(KQueueEventLoop.java:149) at io.netty.channel.kqueue.KQueueEventLoop.kqueueWait(KQueueEventLoop.java:140) at io.netty.channel.kqueue.KQueueEventLoop.run(KQueueEventLoop.java:216) at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:897) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.lang.Thread.run(Thread.java:748) Locked ownable synchronizers: - None ... 12345678910111213141516171819202122232425262728293031323334
另外,在jdk安装路径下执行:
//打开jvisualvm /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home » jvisualvm 12
可打开图形化工具,其中可以获取线程的转储信息。
可用同样方式打开JMC。
竞态
竞态(Race Condition):线程间相互干扰,由脏读和更新丢失导致最终产生结果与预期结果不一致的现象。
计算的正确性依赖于相对时间顺序(Relative Time)或线程的交错(Interleaving)。
分析竞态的方法 - 二维表分析法
竞态产生条件:

  • 读-改-写(read-modify-write): 原因主要为读脏数据-覆盖其他线程对共享变量的更新
  • 检查而后行动(check-then-act): 原因主要为读取变量值,根据读取值决定下一步操作。读取值为脏数据,导致下一步操作出错。

原子性
原子性:

  • 对其他线程,执行线程的状态只有未开始和已完成两种,其他线程无法在执行线程访问(读、写)共享变量时,获取到其中间状态;
  • 访问同一组共享变量的原子操作是不能交错的。

Lock 软件锁;
CAS 硬件锁。
java基本数据类型中,long、double的写操作都不具有原子性。通过添加volatile关键字可以使其具有原子性。
可见性
可见性(Visibility): 程序中变量被分配到寄存器(Register)中处理,一个处理器的寄存器无法读取另一个处理器的寄存器。运行在不同处理器的线程共享变量分配到寄存器进行存储,就会出现可见性问题。
缓存一致性协议(Cache Coherence Protocol): 用于读取其他处理器高速缓存中数据,并更新到该处理器高速缓存中。
缓存同步:一个处理器从自身处理器缓存以外的其他存储部件中读取数据并将其更新到该处理器的高速缓存的过程。
高速缓存、主内存内容是可同步的。
冲刷处理器缓存(写):为保障可见性,必须使一个处理器对共享变量做的更新最终被写入该处理器的高速缓存或主存中的过程。
刷新处理器缓存(读):若共享变量在处理器读取之前进行了更新,该处理器必须从进行更新操作的处理器的高速缓存或主存中将更新的内容进行缓存同步。
保障可见性,可以使用volatile:

  • 提示JIT编译器,被修饰的变量可能被多个线程共享,阻止其做出可能导致程序运行不正常的优化;
  • 读取被修饰的变量会使相应处理器进行刷新处理器缓存处理,写被修饰变量会使相应处理器进行冲刷处理器缓存操作。

相对新值:一个线程更新共享变量后,其他线程能读到更新值,这个值称为变量的相对新值。
最新值:一个线程更新共享变量后,其他线程不能读到更新值,这个值称为变量的最新值。
可见性保障仅意味着线程能够读到共享变量的相对新值,并不能保证该线程能够读到最新值。

20d0d249153d7116d87ca4dd188ee7fe.png


保障原子性,上述操作process2最终读取到的a值可能是0/1/2
保障可见性,上述操作process2最终读取到的a值为2
java规范保证,一个线程终止,其对共享变量的更新,另一个调用期join方法的线程是可见的
有序性
重排序(Reordering):一个处理器上执行多个操作,另一个处理器角度来看可能与目标代码所指定的顺序不一致。
几种内存操作顺序操作:

  • 源代码顺序:未经过编译和解释的源码中指定的内存访问操作顺序
  • 程序顺序:经过编译执行(机器码)或解释执行(字节码Byte Code)中指定的内存访问操作顺序
  • 执行顺序: 内存访问操作在给定处理器上实际执行顺序
  • 感知顺序:给定处理器锁感知到的该处理器及其他处理器的内存访问操作发生的顺序

java平台包含两种编译器:

  • 静态编译器javac:将源代码(.java)编译成字节码(.class二进制文件), 代码编译阶段介入
  • 动态编译器JIT:将字节码动态编译为jaav虚拟机宿主机的本地代码(机器码), java程序运行过程中介入

javac几乎不会执行指令重排序;JIT可能执行指令重排序。查看JIT编译器动态生成的汇编代码
下载hsdis反编译工具,将hsdis-amd64.dylib文件放到/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/server下,和libjvm.dylib同级
文件放置好后,使用命令
java -server -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:+LogCompilation -XX:PrintAssemblyOptions=intel [JAVA 文件路径]
即可看到指定代码的汇编代码内容.
-XX:LogFile=[xxx/xxx.log]可将反编译内容输出到指定文件中
现代处理器:“顺序读取” – “乱序执行” – “顺序提交” 也会导致重排序;
ROB(重排序缓冲器)
内存重排序
Load: 从指定RAM地址(通过高速缓存加载)加载数据到寄存器
Store: 将数据存储到指定地址表示的RAM存储单元

313aae4009c12df05b80079de1d7ede3.png


貌似串行语义(As-if-serial Semantics)
仅保证重排序不影响单线程程序的正确性。
为保证貌似串行语义,存在数据依赖关系的语句不会被重排序。若两个操作(指令)访问同一个变量(地址)且其中一个操作(指令)为写操作,那么两个操作之间就存在数据依赖关系。

a40dacb08db583955823a850b826b2df.png


控制依赖关系: 允许被重排序,若一条语句的执行结果会决定另外一条语句能够被执行,这两条语句存在控制依赖关系。如if语句中的条件表达式和对应的语句体。
从底层角度,禁止(逻辑上)重排序是通过调用处理器提供的相应指令(内存屏障)来实现的。java会替我们与这类指令打交道,我们只需要使用语言本身提供的机制即可。
上下文切换
术语
线程上下文切换: 一个线程被暂停,另一个线程被选中开始或继续运行的过程
时间片: 一个线程可以连续占用处理器运行的时间长度
切入: 一个线程被操作系统选中占用处理器开始或继续运行
切出: 一个线程被剥夺处理器使用权而暂停运行
上下文: 切入和切出时刻相应线程所执行的任务的进行程度(如计算中间结果、执行到哪条指令等),一般包含通用寄存器的内容和程序计数器的内容。
暂停: 线程由RUNNABLE状态转换为非RUNNABLE状态。
唤醒: 线程由非RUNNABLE状态转换为RUNNABLE状态。
被唤醒的线程并非立即占用处理器运行,当被唤醒的线程被操作系统选中占用处理器继续运行时,操作系统才会恢复其上下文。
自发性上下文切换:
由自身因素导致的切出,如下方法会导致自发性上下文切换

da309dcba4efaf244cf246b7d6015fbd.png


I/O操作或等待其他线程持有的锁
非自发性上下文切换:
由于线程调度器的原因被迫切出

  • 被切出线程时间片用完
  • 一个优先级更高的线程需要被运行
  • java虚拟机垃圾回收动作

开销及测量
直接开销:

  • 操作系统保存和恢复上下文所需开销
  • 线程调度器进行线程调度的开销

间接开销:

  • 处理器高速缓存重新加载的开销,切出线程被另一个处理器切入,继续运行,若新处理器从未运行过该线程,需要重新从主存货通过缓存一致性协议将线程运行过程中所需变量加载到高速缓存中
  • 可能导致整个一级缓存中的内容被冲刷(Flush),内容被写入下一级高速缓存或主存中

测量:
确定一个多线程程序在某个时间段或某种场景下运行时发生的上下文切换(主要是自发性上下文切换)的次数。

  • Linux平台下,使用其内核提供的perf命令来监视java程序运行过程中的上下文切换次数和频率。
    eg:perf stat -e cpu-clock, task-clock, cs, cache-references, cache-misses java [Main Class]
    其中参数e的值中,cs表示被监视程序的上下文切换的数量。
  • windows平台下,perform命令

线程的活性故障
由于资源稀缺性或程序自身的问题和缺陷导致线程一直处于非RUNNABLE状态,或状态处于RUNNABLE状态但是其要执行的任务一直无法进展的现象就被称为线程活性故障

  • 死锁(Deadlock): 两个线程互相等待对方释放资源
  • 锁死(Lockout): 执行所需获取的资源一直未被释放
  • 活锁(Livelock): 线程处于RUNNABLE状态,但要执行的任务没有进展
  • 饥饿(Starvation): 因无法获得其所需资源而使得任务执行无法进展

资源争用与调度
排他性资源:一次只能够被一个线程占用的资源,如处理器、数据库连接、文件等。
资源争用:一个线程占用一个排他性资源进行读写操作而未释放其对资源所有权的时候,其他线程试图访问该资源的现象。
高/低争用:同时试图访问同一个已经被其他线程占用的资源的线程数量多/少。
高并发:处于运行状态(RUNNABLE的子状态RUNNING)的线程数量多,程序运行理想的状态为高并发、低争用。
资源调度:多个线程申请同一个排他性资源的情况下,决定哪个申请者占用该资源的过程。常见特性是它是否能保证公平性。
公平性:资源的申请者是否按照其申请资源的顺序而被授予资源的独占权。
排队:常见资源调度策略,调度器持有一个等待队列,未获取独占权的线程进入队列暂停,待资源释放被唤醒,从队列中移除,若再次申请资源失败,再次进入队列中暂停;由此可见,资源调度可能导致上下文切换。
公平调度策略:资源未被其他任何线程占用,等待队列为空的情况,资源的申请者才被允许抢占相应资源的独占权。抢占失败的申请者进入等待队列。此策略中资源申请者总是按照先来后到的顺序获得资源的独占权。
非公平调度策略:允许插队。一个线程释放其资源独占权的时候,等待队列中的一个线程会被唤醒再次申请相应的资源,而在这个过程中另外一个申请该线程的活跃线程可以与这个被唤醒的线程共同参与相应资源的抢占。可能导致饥饿现象。吞吐量更高,但申请者获取相应资源的独占权所需的时间偏差可能较大。
非公平调度策略公平调度策略适用于多数线程占用资源较短或资源平均申请时间间隔相对较短的场景适用于多数线程占用资源较长或资源平均申请时间间隔相对较长的场景吞吐率较大吞吐率较小可能导致饥饿状态不会导致饥饿状态
等待队列中的线程从被唤醒到继续运行可能需要一段时间,此间新来线程若占用该资源时间不长,完全有可能在被唤醒线程继续执行之前将资源释放,此时可能减少了上下文切换次数。若新来线程占用资源时间太长,被唤醒资源需要重新被暂停进入等待队列中,增加了上下文切换次数。

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

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

相关文章

vue企业网站模板_模板网站VS定制网站,企业如何选择?

步入互联网时代,企业需要建设属于自己的网站,网站建设到底是选择模板网站,还是定制网站呢?笔者接下来跟大家详细分析一下。网站建设的类型一、网站建设的类型模板网站模板型网站是用已经成形的网站为框架进行套用,根据…

这张磁盘有写保护_架构师不得不了解的硬件知识 - 磁盘阵列RAID

概述什么是RAID? RAID ( Redundant Array of Independent Disks )即独立磁盘冗余阵列,通常简称为磁盘阵列。简单地说, RAID 是由多个独立的高性能磁盘驱动器组成的磁盘子系统,从而提供比单个磁盘更高的存储…

金蝶二次开发好跳槽吗_金蝶财务软件不会操作怎么办?

还在为不会操作金蝶软件而头疼吗?今天,小编和大家分享一些金蝶软件实操技巧,让大家更快速的完成工作!接下来,跟着小编一起来学习吧~1、新增凭证快捷键2、修改、删除凭证3、凭证审核说明:4、凭证过账说明&a…

可信计算3.0工程初步pdf_查校 | 英国大学工业工程与运筹学专业40个授课硕士+研究Mphil/Phd 项目汇总...

关注“留学壹周刊”,回复专业名称,如“金融”,可以自由查询相关资料介绍本篇微信主要包括如下内容:1、2020Fall英国工业工程与运筹学专业授课类硕士(共26个项目)2、2020Fall英国工业工程与运筹学专业研究类PHD(共14个项目)3、2020…

怎么把此电脑放到桌面_Win10我的电脑怎么放到桌面

1/5点击右键选择“个性化”2/5进入后,点击“主题”3/5在主题页面的右侧点击“桌面图标设置”4/5最后勾选“计算机”5/5再点击“确定”即可

vmware虚拟机安装win7_图文分享虚拟机怎么安装win7系统

最近有朋友问有什么方法可以在不重装系统的情况下体验到其他的操作系统。那就是使用虚拟机吧,下面就以win7为例给大家分享一下在虚拟机安装WIN7的步骤。1,打开运行虚拟机,然后点击界面左上方的“文件”按钮,菜单栏选择“新建虚拟机…

usb接口供电不足_1个USB接口变成4个?什么东西那么“牛”?请你花2分钟了解一下...

USB分线器可以说是许多电脑用户日常生活中必不可少的配件,有了这东西,能让你的USB接口多出许多,关键是通过延长线可以把USB接口放置在你随手可得的地方,可谓是一款家庭、办公都很实用的小配件。USB分线器通常可以分为4、6、8个USB…

js中的new file_JS中的new操作符

一、什么是new?众所周知,在JS中,new的作用是通过构造函数来创建一个实例对象。像下面这样:(和普通函数不一样,当函数用作 构造函数 时,首字母一般要大写,以作区分。)function Foo(name) { th…

css不继承父类样式_提升你的Python编程能力,细说类的继承,拿走不谢

今天咱们聊点啥呢?要不还是聊点Python基础的东东吧,大家已经都知道啦,Python呢是一种面向对象的脚本编程语言,那面向对象的编程语言呢,都会支持面向对象的三大特性,你知道是哪三大特性吗?当然是…

R 回归 虚拟变量na_【R语言进阶】Logistic回归及哑变量设置

点击上方“蓝字”,我们一起分析数据Logistic回归是最常用的多因素回归模型,在医学研究中,常用于研究疾病的危险因素,下面我们一起来看看,R语言是如何实现Logistic回归的。1第一步 导入数据首先,在excel里全选数据集,右键复制。然后&#xff0…

logisticregression参数_通俗地说逻辑回归【Logistic regression】算法(二)sklearn逻辑回归实战...

前情提要:通俗地说逻辑回归【Logistic regression】算法(一) 逻辑回归模型原理介绍上一篇主要介绍了逻辑回归中,相对理论化的知识,这次主要是对上篇做一点点补充,以及介绍sklearn 逻辑回归模型的参数&#…

tensorflow适用于python版本_tensorflow用python哪个版本更好?

tensorflow用python哪个版本?一、安装anaconda tensorflow是基于python脚本语言的,因此需要安装python, 当然还需要安装numpy、scipy、six、matplotlib等几十个扩展包。如果一个个安装,装到啥时候去?(我曾经光安装scip…

php parse url ctf,【SSRF】如何绕过filter_var(), preg_match() 和 parse_url()

0x01 前言这篇文章是在我看完一片国外安全大佬写的文章后对其进行总结并翻译得到的。0x02 正文之绕过filter_var和preg_match本片文章主要深入一种php ssrf的技术——如何绕过例如filter_var(), preg_match()和parse_url()等函数。本次我进行测试的php版本全部为php v5.6.30php…

python json解析_python读取json文件并解析

原博文 2018-07-09 18:35 − # -*- coding: utf-8 -*- import os import json import sys reload(sys) sys.setdefaultencoding(utf-8) filelistos.listdir(E:\\log\\files\\) for ite... 相关推荐 2019-12-05 20:03 − 如何使用 Python 语言来编码和解码 JSON 对象。 JSON(Jav…

python中文词云图代码_Python简单实现词云图代码及步骤解析

一、安装 wordcloud pip install wordcloud 二、加载包、设置路径 import os from wordcloud import WordCloud import matplotlib.pyplot as plt os.chdir(E:\\pyspace\\tmp) 三、词云图示例 1、默认参数示例 text Keep it simple and stupid. wc WordCloud() # 实例化词云图…

usb大容量存储设备驱动程序_20年历史了!为什么USB接口还存在?网友:原来如此...

USB接口作为计算机领域应用最广泛的数据接口,已有20多年的历史。和它的名字一样,USB(Universal Serial Bus)最初也是为统一的数据接口而设计的。你几乎可以用它来代替计算机的各种外部数据接口,只需为它设计相应的驱动程序。随着市场对USB接口…

linux终端炫酷命令,你不得不知道11个炫酷的 Linux 终端命令

很多朋友都很喜欢Linux ,Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统,Linux是一款免费的操作系统,用户可以通过网络或其他途径免费获得,并可…

lasso回归_一文读懂线性回归、岭回归和Lasso回归

(图片由AI科技大本营付费下载自视觉中国)作者 | 文杰编辑 | yuquanle本文介绍线性回归模型,从梯度下降和最小二乘的角度来求解线性回归问题,以概率的方式解释了线性回归为什么采用平方损失,然后介绍了线性回归中常用的两种范数来解决过拟合和…

springcloud架构特点_打造企业级微服务平台架构,分布式应用场景管理

微服务平台架构是一项在云中部署应用和服务的新技术。大部分围绕微服务的争论都集中在容器或其他技术是否能很好的实施微服务。微服务系统可以在“自己的程序”中运行,并通过“轻量级设备与HTTP型API进行沟通”。关键在于该服务可以在自己的程序中运行。通过这一点我…

怎么判断一个字符串的最长回文子串是否在头尾_LeetCode 5 迅速判断回文串的Manacher算法...

本文始发于个人公众号: TechFlow题意Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.Link: https://leetcode.com/problems/longest-palindromic-substring/翻译给定一个字符串s&#xff0c…