想玩转JAVA高并发,这些概念你必须懂
高并发高并发
它是互联网分布式系统架构设计中必须考虑的因素之一,通常是指,保证系统能够同时并行化处理海量请求
同步和异步
- 同步:发送一个请求,等待返回,然后再发送下一个请求。提交请求 -> 等待服务器处理 -> 处理完返回,此期间客户端浏览器不能干任何事
- 异步:发送一个请求,不等待返回,随时可以再发送下一个请求。提交请求 -> 服务器处理(这时浏览器仍然可以做其他事情)-> 处理完毕
从上图可以知道,随着实时间的轨迹,同步一步一步的执行着,在异步中,当一个异步过程调用发出后,调用者不能立即得到结果,实际上会开启一个线程执行这部分内容,这个线程处理完了之后,通过状态,通知和回调来通知调用者来处理。
并发和并行
单核CPU(单处理器)上,只可能存在并发而不可能存在并行。 并行在多处理器系统中存在,而并发可以在单处理器和多处理器系统中都存在,并发能够在单处理器系统中存在是因为并发是并行的假象,并行要求程序能够同时执行多个操作,而并发只是要求程序假装同时执行多个操作(每个小时间片执行一个操作,多个操作快速切换执行
临界区
临界区用来表示一种公共资源或者说是共享数据,可以被多个线程使用,但是每一次,只能有一个线程使用它,一旦临界去资源被占用,其他线程想要使用这个资源,就必须等待。
这就是我们编程中经常要加锁的地方,如 Synchronized 关键字,或是 Lock 接口。
阻塞和非阻塞
- 阻塞(Blocking)和非阻塞(Non-Blocking)通常用来形容多线程间的相互影响,比如一个线程占用临界区资源,那么其他所有需要这个资源的线程就必须在这个临界区中进行等待,等待会导致线程挂起,这种情况就是阻塞。如果占用资源的线程一直不愿意释放资源,那么其它所有阻塞在这个临界区上的线程都不能工作。
- 非阻塞允许多个线程同时进入临界区。
死锁、饥饿、活锁
死锁: 指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
互斥条件:线程对资源的访问是排他性的,如果一个线程对占用了某资源,那么其他线程必须处于等待状态,直到资源被释放。 请求和保持条件:线程T1至少已经保持了一个资源R1占用,但又提出对另一个资源R2请求,而此时,资源R2被其他线程T2占用,于是该线程T1也必须等待,但又对自己保持的资源R1不释放。 不剥夺条件:线程已获得的资源,在未使用完之前,不能被其他线程剥夺,只能在使用完以后由自己释放。 环路等待条件:在死锁发生时,必然存在一个“进程-资源环形链”,即:{p0,p1,p2,…pn},进程p0(或线程)等待p1占用的资源,p1等待p2占用的资源,pn等待p0占用的资源。(最直观的理解是,p0等待p1占用的资源,而p1而在等待p0占用的资源,于是两个进程就相互等待
活锁: 指线程T1可以使用资源,但它很礼貌,让其他线程先使用资源,线程T2也可以使用资源,但它很绅士,也让其他线程先使用资源。这样你让我,我让你,最后两个线程都无法使用资源。
在街上遇到一妹子,刚好她朝着你的反方向走,与你正面碰到,你们都想让彼此过去。你往左边移,她也往左边移,两人还是无法过去。这时你往右边移,她也往右边移,如此循环下去。
饥饿: 指如果线程T1占用了资源R,线程T2又请求封锁R,于是T2等待。T3也请求资源R,当T1释放了R上的封锁后,系统首先批准了T3的请求,T2仍然等待。然后T4又请求封锁R,当T3释放了R上的封锁之后,系统又批准了T4的请求……,T2可能永远等待。
有两条道A和B上都堵满了车辆,其中A道堵的时间最长,B相对相对堵的时间较短,这时,前面道路已疏通,交警按照最佳分配原则,示意B道上车辆先过,B道路上过了一辆又一辆,A道上排队时间最长的确没法通过,只能等B道上没有车辆通过的时候再等交警发指令让A道依次通过,这也就是 ReentrantLock 显示锁里提供的不公平锁机制(当然了,ReentrantLock 也提供了公平锁的机制,由用户根据具体的使用场景而决定到底使用哪种锁策略),不公平锁能够提高吞吐量但不可避免的会造成某些线程的饥饿。
并发级别
分为 阻塞 和 非阻塞(非阻塞分为无障碍、无锁、无等待)
阻塞
当一个线程进入临界区后,其他线程必须等待
无障碍
- 无障碍是一种最弱的非阻塞调度
- 可自由出入临界区
- 无竞争时,有限步内完成操作
- 有竞争时,回滚数据
和非阻塞调度相比呢,阻塞调度是一种悲观的策略,它会认为说一起修改数据是很有可能把数据改坏的。而非阻塞调度呢,是一种乐观的策略,它认为大家修改数据未必把数据改坏。 但是它是一种 宽进严出 的策略,当它发现一个进程在临界区内发生了数据竞争,产生了冲突,那么无障碍的调度方式则会回滚这条数据。
在这个无障碍的调度方式当中,所有的线程都相当于在拿去一个系统当前的一个快照。他们一直会尝试拿去的快照是有效的为止。
无锁
- 是无障碍的
- 保证有一个线程可以胜出 与无障碍相比,无障碍并不保证有竞争时一定能完成操作,因为如果它发现每次操作都会产生冲突,那它则会不停地尝试。如果临界区内的线程互相干扰,则会导致所有的线程会卡死在临界区,那么系统性能则会有很大的影响。
而无锁增加了一个新的条件,保证每次竞争有一个线程可以胜出,则解决了无障碍的问题。至少保证了所有线程都顺利执行下去。
下面代码是Java中典型的无锁计算代码
while (!atomicVar.compareAndSet(localVar, localVar+1)) { localVar = atomicVar.get();}
无等待
- 无锁的
- 要求所有的线程都必须在有限步内完成
- 无饥饿的
无等待的前提是无锁的基础上的,无锁它只保证了临界区肯定有进也有出,但是如果进的优先级都很高,那么临界区内的某些优先级低的线程可能发生饥饿,一直出不了临界区。那么无等待解决了这个问题,它保证所有的线程都必须在有限步内完成,自然是无饥饿的。
无等待是并行的最高级别,它能使这个系统达到最优状态。无等待的典型案例:只有读线程,没有写线程,那么这个则必然是无等待的。 如果既有读线程又有写线程,而每个写线程之前,都把数据拷贝一份副本,然后修改这个副本,而不是修改原始数据,因为修改副本,则没有冲突,那么这个修改的过程也是无等待的。最后需要做同步的只是将写完的数据覆盖原始数据。由于无等待要求比较高,实现起来比较困难,所以无锁使用得会更加广泛一些。
关于并行的2个重要定律
两个定律都与加速比有关
阿姆达尔定律
Amdahl定律(阿姆达尔定律):定义了串行系统并行化后的加速比的计算公式和理论上限(加速比=优化前系统耗时/优化后系统耗时) 一个程序(或者一个算法)可以按照 是否可以被并行化 分为下面两个部分:
- 可以被并行化的部分
- 不可以被并行化的部
假设一个程序处理磁盘上的文件。这个程序的一小部分用来扫描路径和在内存中创建文件目录。做完这些后,每个文件交个一个单独的线程去处理。扫描路径和创建文件目录的部分不可以被并行化,不过处理文件的过程可以。
增加CPU处理器的数量并不一定能起到有效的作用,提高系统内可并行化的模块比重,合理增加并行处理器数量,才能以最小的投入,得到最大的加速比
古斯塔夫森定律
Gustafson定律(古斯塔夫森):说明处理器个数,串行比例和加速比之间的关系
只要有足够的并行化,那么加速比和CPU个数成正比
想玩转JAVA高并发,这些概念你必须懂的更多相关文章
- 想玩转JAVA高并发,这些概念你必须懂!
我们在找工作时,经常在招聘信息上看到有这么一条:有构建大型互联网服务及高并发等经验,你第一时间想到的是媒体常说的双十一吗?带着问题,我们一起思考技术…. 高并发高并发 它是互联网分布式系统架构设计中必 ...
- java高并发系列-第1天:必须知道的几个概念
java高并发系列-第1天:必须知道的几个概念 同步(Synchronous)和异步(Asynchronous) 同步和异步通常来形容一次方法调用,同步方法调用一旦开始,调用者必须等到方法调用返回后, ...
- 跟着阿里p7一起学java高并发 - 第18天:玩转java线程池,这一篇就够了
java中的线程池,这一篇就够了 java高并发系列第18篇文章. 本文主要内容 什么是线程池 线程池实现原理 线程池中常见的各种队列 自定义线程创建的工厂 常见的饱和策略 自定义饱和策略 线程池中两 ...
- java高并发系列 - 第4天:JMM相关的一些概念
JMM(java内存模型),由于并发程序要比串行程序复杂很多,其中一个重要原因是并发程序中数据访问一致性和安全性将会受到严重挑战.如何保证一个线程可以看到正确的数据呢?这个问题看起来很白痴.对于串行程 ...
- java高并发系列 - 第6天:线程的基本操作
新建线程 新建线程很简单.只需要使用new关键字创建一个线程对象,然后调用它的start()启动线程即可. Thread thread1 = new Thread1(); t1.start(); 那么 ...
- 跟着阿里p7一起学java高并发 - 第19天:JUC中的Executor框架详解1,全面掌握java并发核心技术
这是java高并发系列第19篇文章. 本文主要内容 介绍Executor框架相关内容 介绍Executor 介绍ExecutorService 介绍线程池ThreadPoolExecutor及案例 介 ...
- java高并发系列 - 第13天:JUC中的Condition对象
本文目标: synchronized中实现线程等待和唤醒 Condition简介及常用方法介绍及相关示例 使用Condition实现生产者消费者 使用Condition实现同步阻塞队列 Object对 ...
- Java高并发系列——检视阅读
Java高并发系列--检视阅读 参考 java高并发系列 liaoxuefeng Java教程 CompletableFuture AQS原理没讲,需要找资料补充. JUC中常见的集合原来没讲,比如C ...
- Java高并发与多线程(四)-----锁
今天,我们开始Java高并发与多线程的第四篇,锁. 之前的三篇,基本上都是在讲一些概念性和基础性的东西,东西有点零碎,但是像文科科目一样,记住就好了. 但是本篇是高并发里面真正的基石,需要大量的理解和 ...
随机推荐
- 在Visual Studio 中使用git——文件管理-中(五)
在Visual Studio 中使用git--什么是Git(一) 在Visual Studio 中使用git--给Visual Studio安装 git插件(二) 在Visual Studio 中使用 ...
- table不能使用jQuery的slideDown的解决方法
table不能使用jQuery的slideDown的解决方法 一个后台管理项目中遇到了如下场景 要求父级栏目可以收纳子栏目,即折叠功能,而且要有过渡动画,不能太生硬. 它是用table来实现的,但是遇 ...
- [c++] 声明
注意事项 变量定义时就会赋初值(如int是0),所以定义包含声明,单纯声明不定义的话,要加extern 同一个文件中,函数使用(调用)前必须定义(有{ }为定义),否则会报错:若先使用再定义,或定义和 ...
- [刷题] 剑指Offer 面试题7:重建二叉树
题目:输入某二叉树的前序遍历和中序遍历结果,重建该二叉树.(假设输入的前序和中序遍历结果中都不含重复数字) 思路 构建二叉树的两个函数:Construct().ConstructCore() Cons ...
- [Qt] 打包
步骤: 1.release项目,生成exe文件 2.在命令行中使用windeployqt,将相关文件复制到exe文件所在文件夹 3.用Enigma Virtual Box打包所有文件 参考 https ...
- tar cf XXX.tar /tmp /var 日志保存
tar cf XXX.tar /tmp /var 检测tar tf XXX.tar /tmp /var
- debian配置---->/etc/apt/sources.list apt基本源设置指南
yum或apt基本源设置指南 关于: 管理Linux服务器的运维或开发人员经常需要安装软件,最常用方式应该是通过Linux系统提供的包管理工具来在线安装,比如centos的yum,ubuntu或d ...
- Java EnumMap 实现类
EnumMap 实现类 因为 HashMap 是一种通过对 key 计算 hashCode(),通过空间换时间的方式,直接定位到 value 所在的内部数组的索引,因此,查找效率非常高. 如果作为 k ...
- 6.5 scp:远程文件复制
scp命令 用于在不同的主机之间复制文件,它采用SSH协议来保证复制的安全性.scp命令每次都是全量完整复制,因此效率不高,适合第一次复制时使用,增量复制建议使用rsync命令替代. scp ...
- redis 处理缓存击穿以及缓存雪崩
缓存击穿 1. 缓存击穿简述 某一个热点数据在缓存中失效,请求穿过redis到达DB,造成DB压力过大 2. 怎么解决缓存击穿 1. 使用redis 作为分布式互斥锁(mutex lock) 实现步骤 ...