《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日) ...
随机推荐
- 解决使用(Jenkins检出代码)git clone检出代码提示必须安装 .NET framework,Version =v4.7.2
一.事件背景 真的是非常想使用pipeline流水线进行自动化部署打包测试. 于是,晚上下班回家后,真的是"现学现卖",开始做流水线脚本. 经过不懈努力,熬到凌晨两点多,终于把整个 ...
- 盘点Vue2和Vue3的10种组件通信方式(值得收藏)
Vue中组件通信方式有很多,其中Vue2和Vue3实现起来也会有很多差异:本文将通过选项式API 组合式API以及setup三种不同实现方式全面介绍Vue2和Vue3的组件通信方式.其中将要实现的通信 ...
- 垃圾收集器 参阅<<深入理解JAVA虚拟机>>
一.新生代 1.Serial收集器 新生代单线程复制算法GC(暂停工作线程)---------- 支持组合老年代Serial odl和CMS 2.ParNew Serial多线程版本 支持组合cms| ...
- P5384[Cnoi2019]雪松果树 (长链剖分)
题面 一棵以 1 1 1 为根的 N N N 个节点的有根树, Q Q Q 次询问,每次问一个点 u u u 的 k k k 级兄弟有多少个(第 k k k 代祖先的第 k k k 代孩子),如果没有 ...
- 3-12 Python函数定义与调用
Python 函数 函数概念 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率.如print() range()函数,但你也可以自己创建函 ...
- Filter(过滤器)、ThreadLocal(本地线程)、Listener(监听器)
Filter(过滤器) Filter过滤器它的作用是:拦截请求,过滤响应. 过滤器链 1)执行的顺序依次是: A B C Demo03 C2 B2 A2 2)如果采取的是注解的方式进行配置,那么过滤器 ...
- python数据精度问题
一.python运算时精度问题: 1.运行时精度问题在Python中(其他语言中也存在这个问题,这是计算机采用二进制导致的),有时候由于二进制和十进制之间对应问题会导致数值的精度问题,比如无法用有限个 ...
- Databend 源码阅读系列(二):Query server 启动,Session 管理及请求处理
query 启动入口 Databend-query server 的启动入口在 databend/src/binaries/query/main.rs 下,在初始化配置之后,它会创建一个 Global ...
- 【lwip】06-网络接口层分析
目录 前言 6.1 概念引入 6.2 网络接口层数据概念流图 6.3 网卡收包程序流图 6.4 网卡数据结构 6.4.1 struct netif源码 6.4.2 字段分析 6.4.2.1 网卡链表 ...
- 01_Django-介绍-项目结构-URL和视图函数
01_Django-介绍-项目结构-URL和视图函数 视频:https://www.bilibili.com/video/BV1vK4y1o7jH 博客:https://blog.csdn.net/c ...