《Java并发编程的艺术》读书笔记:一、并发编程的目的与挑战
发现自己有很多读书笔记了,但是一直都是自己闷头背,没有输出,突然想起还有博客圆这么个好平台给我留着位置,可不能荒废了。
此文读的书是《Jvava并发编程的艺术》,方腾飞等著,非常经典的一本书,本笔记是一边复习笔记,一边对照书本上的勾画重点,互相补充得来。
一、并发编程的目的与挑战
并发编程的目的只有一个:更充分的利用好计算资源,让程序运行的更快更有效率
请把这个目的牢记在心,因为,并不是启动更多的线程就能让程序最大限度的并发执行的,因为配合,甚至管理更多的线程本身也是有开销的。所以至少来说,利用并发编程来提升程序效率还面临着以下几项挑战:
- 上下文切换
- 死锁
- 硬件与软件资源限制
1、上下文切换
什么是上下文切换?考虑一个CPU流水线式处理多个进程的情况,我们知道。CPU通过轮流分配给各个进程以一定的时间片来实现并发执行,那么当CPU在作出时间片的切换时,必须要保存上一个任务的状态,以便后续接着上次的位置进行处理,随后再将下一个任务之前运行到的状态(如果有的话)读取加载进入CPU,这就是上下文切换,很明显,这样的切换是会影响效率的。
为什么这样的切换影响效率?
因为CPU是整个计算机结构中处理速度最快的结构,在整个计算机组成结构中,距离CPU越近的存储器就越是寸土寸金,自然不能大量留给那些已经被换下CPU的进程。
通常来说,换下CPU的进程的上下文会作为匿名页保存在内存当中,而内存与距离CPU最近的高速缓存有100倍的访问速度差距,更不用提寄存器了,所以频繁的进行上下文切换必然是会带来性能损失的
考虑到CPU的时间片往往持续非常短,一秒钟就可能需要上千次上下文切换,如何避免它就成为了我们提高并发效率的核心。
减少上下文切换,比较常用的办法有:
- 无锁并发编程
- CAS算法
- 使用最少线程编程
- 使用协程
下面具体来看。
无锁并发编程
由于多线程竞争锁时,会引起上下文切换,所以多线程处理数据时可以尽量避免锁。比如可以根据数据ID取模来将数据分段,然后不同线程处理不同段的数据
CAS算法
Java的Atomic包就是使用CAS算法来更新数据的,这种办法不需要加锁。
CAS (Compare And Swap 比较交换算法),其实现方式是基于硬件平台的汇编指令,在intel的CPU中,使用的是cmpxchg
指令,也就是说CAS是靠硬件实现的,从而在硬件层面提升效率。这里还需要引入两个概念:
- 乐观锁,总是认为当前是线程安全的,不怕别的线程修改变量,所以并不真正上锁。只是在每次读写时判断一下临界区是否已经被其他线程修改,如果修改了,就(通常是通过循环)再重新读取被修改后的数据再处理。CAS算法就属于这类
- 悲观锁:总是认为当前是线程不安全的,不管什么情况都进行加锁,任何线程再读写时若获取锁失败,则阻塞。许多经典的锁机制如
synchronized
关键字都属于这类方法。
既然CAS算法是乐观锁的一种,那么也就是说,CAS并不会真的在并发时给临界区加锁,那么如何保证不会出现错误呢?下面来看CAS算法的原理,如图所示,当线程开启时,会从主存中给每个线程拷贝一个变量副本到线程各自的运行环境中,CAS算法中包含三个参数(V,E,N),V表示主存中该变量的值、E表示之前拷贝到的预期值、N表示此次更新后的新值。
那么现在有两个线程t1,t2。他们各自的运行环境中都有共享变量的副本V1、V2,预期值E1、E2,预期主存中的值还没有被改变,假设现在在并发环境,t1先拿到了执行权限,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次发起尝试(只要还处于时间片中),然后t1比较预期值E1和主存中的V,发现E1=V,说明预期值是正确的,执行N1=V1+1,并将N1的值传入主存。这时候贮存中的V=21,然后t2又紧接着拿到了执行权,比较E2和主存V的值,由于V已经被t1改为21,所以E2!=V,t2线程将主存中已经改变的值更新到自己的副本中,再发起重试;直到预期值等于主存中的值,说明没有别的线程对旧值进行修改,则继续执行代码。
CAS算法很重要,它是Java并发编程里很多技术的基石。
使用最少线程
简单直白,我们避免创建不必要的线程,从而避免出现大量线程等待的情况。
协程
在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。
2、死锁
显然在多线程编程中,锁是一种非常有用的工具,其运用场景极多,因为它使用简单且易于理解。不过,一旦滥用锁就会大大折损效率,更可怕的是有可能会造成死锁。
死锁要发生需要满足四个条件,缺一不可:
- 互斥条件
- 即目标资源是不可共享的,多个进程不能同时持有该资源
- 持有并等待条件
- 一旦某个进程持有该资源,则在该进程运行结束前,不会主动释放资源
- 不可夺占条件
- 其他进程不能抢夺已经被某个进程所持有的资源
- 循环等待条件
- 进程之间获取这些互斥资源的顺序形成了环形链
死锁本身很简单,就是因为某些原因导致两把锁或者更多锁的持有进程在相互等待,永无止境。解决死锁不难,只要破坏死锁四大条件中的任意一条即可。
一般而言我们有以下方法:
- 避免一个线程同时获取多个锁
- 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源
- 尝试使用定时锁,即
lock.tryLock(timeout)
替代内部锁机制 - 对于数据库锁,加锁与解锁必须在一个数据库链接里,否则可能出现解锁失败的情况
- 保证需要请求互斥资源的进程都以完全相同的顺序去请求资源
3、资源限制
资源限制表示由于硬件或软件的硬性指标不能支持过高并发而引起的性能限制。最简单的例子就是,如果网络就2Mb/s,而某个资源的下载速度是1Mb/s,你即便启动10个线程来进行下载,也不可能达到10Mb/s。而对于并发程序来说,网络带宽,硬盘读写速度,CPU,GPU处理速度,软件资源限制以及socket连接数等等都可能成为这个障碍。
而要解决这个问题,我们能做的只有开源与节流
- 开源:尝试获取更多的资源,一台计算机无法处理,可以通过Hadoop等平台搭建一个计算机集群,通过大量计算机一同处理。其他资源同理
- 节流:根据我们所持有的资源量,设定一个合理的并发度,控制线程数量在一个合适的范围中
《Java并发编程的艺术》读书笔记:一、并发编程的目的与挑战的更多相关文章
- Java并发编程的艺术读书笔记(2)-并发编程模型
title: Java并发编程的艺术读书笔记(2)-并发编程模型 date: 2017-05-05 23:37:20 tags: ['多线程','并发'] categories: 读书笔记 --- 1 ...
- Java并发编程的艺术读书笔记(1)-并发编程的挑战
title: Java并发编程的艺术读书笔记(1)-并发编程的挑战 date: 2017-05-03 23:28:45 tags: ['多线程','并发'] categories: 读书笔记 --- ...
- synchronized的实现原理-java并发编程的艺术读书笔记
1.synchronized实现同步的基础 Java中的每个对象都是可以作为锁,具体有3种表现. 1.对于普通同步方法,锁是当前实例对象. 2.对于静态同步方法,锁是当前类的Class对象. 3.对于 ...
- 《Java并发编程实战》读书笔记一 -- 简介
<Java并发编程实战>读书笔记一 -- 简介 并发的历史 并发的历史,也是人类利用有限的资源去提高生产效率的一个的例子. 设想现在有台计算机,这台计算机具有以下的资源: 单核CPU一个 ...
- 《Java编程思想》读书笔记(二)
三年之前就买了<Java编程思想>这本书,但是到现在为止都还没有好好看过这本书,这次希望能够坚持通读完整本书并整理好自己的读书笔记,上一篇文章是记录的第一章到第十章的内容,这一次记录的是第 ...
- 《Java编程思想》读书笔记(四)
前言:三年之前就买了<Java编程思想>这本书,但是到现在为止都还没有好好看过这本书,这次希望能够坚持通读完整本书并整理好自己的读书笔记,上一篇文章是记录的第十七章到第十八章的内容,这一次 ...
- 《Java编程思想》读书笔记(五)
前言:本文是<Java编程思想>读书笔记系列的最后一章,本章的内容很多,需要细读慢慢去理解,文中的示例最好在自己电脑上多运行几次,相关示例完整代码放在码云上了,码云地址:https://g ...
- 《Go并发编程实战》读书笔记-语法概览
<Go并发编程实战>读书笔记-语法概览 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 本篇博客我们会快速浏览一下Go的语法,内容涉及基本构成要素(比如标识符,关键字,子 ...
- 《Go并发编程实战》读书笔记-初识Go语言
<Go并发编程实战>读书笔记-初识Go语言 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在讲解怎样用Go语言之前,我们先介绍Go语言的特性,基础概念和标准命令. 一. ...
- 《Linux/Unix系统编程手册》读书笔记 目录
<Linux/Unix系统编程手册>读书笔记1 (创建于4月3日,最后更新4月7日) <Linux/Unix系统编程手册>读书笔记2 (创建于4月9日,最后更新4月10日) ...
随机推荐
- 八个解决你80%需求的CSS动画库
八个解决你80%需求的CSS动画库 点击打开视频讲解 在学习和工作的过程中,我们总免不了要写各种各样的css动画,给某个部分添加动画效果,如果觉得自己写的动画效果单一乏味,不妨试试这8个CSS动画库, ...
- SP6779 GSS7 - Can you answer these queries VII(线段树,树链剖分)
水题,只是坑点多,\(tag\)为\(0\)时可能也要\(pushdown\),所以要\(bool\)标记是否需要.最后树链剖分询问时注意线段有向!!! #include <cstring> ...
- React报错之Type '() => JSX.Element[]' is not assignable to type FunctionComponent
正文从这开始~ 总览 当我们尝试从函数组件中返回元素组成的数组时,会产生"Type '() => JSX.Element[]' is not assignable to type Fu ...
- java学习第一天.day01
Java的编译和运行机制 java文件编译成字节码文件后加载到java缓存中jvm Java的基本语法 1.Java语言严格区分大小写 2.一个Java源文件里可以定义多个Java类,但不能存在多个p ...
- Linux —用户和组
Linux 用户和组 1.用户和组的概念 用户的作用: Authentication:认证 Authorization:授权 Accouting:审计 用户存在的最终目的: 为了实现资源的分派 组的作 ...
- 一个监控工具monit
最近看了一个问题,monit监控在读取配置文件之后,访问的文件是残留在容器中的,导致认为服务异常,其实一开始容器启动,并没有对应的服务. [root@10-36-235-119.fin-wealth- ...
- flutter系列之:用来管理复杂状态的State详解
目录 简介 StatefuWidget和State State的生命周期 总结 简介 Flutter的基础是widget,根据是否需要跟用户进行交互,widget则可以分为StatelessWidge ...
- 【lwip】005-lwip内核框架剖析
目录 前言 5.1 lwip初始化 5.2 内核超时 5.2.1 内核超时机制 5.2.2 周期定时机制 5.2.3 内核超时链表数据结构 5.2.4 内核超时初始化 5.2.6 超时的溢出处理 5. ...
- CF-1453B
Problem - 1453B - Codeforces 题意: 一个数组,每次可以选择一个后缀,将其加一或者减一,开始的时候可以免费改变一个数的数值,判断让所有数字相等所需要的最小操作数. 题解: ...
- OSI七层模型与TCP/IP协议
作者:菘蓝 时间:2022/9/1 ================================================================================== ...