Java高并发系列(读书笔记)——相关的重要概念和并发级别

相关的重要概念


(1)同步(synchronous)和异步( asynchronous)、阻塞(blocking)和非阻塞(non-blocking)

关于这个可以看同步、异步和阻塞、非阻塞的区别与理解,这里有较为详细的解释。

对于同步和异步,举个栗子,比如你中午要去外面吃饭,你在前台点好菜后就在座位上等待服务员上菜,前台就会根据你点的菜单交厨师做好菜来,于是你就在座位上慢慢等待菜做好端上来,直到厨师做好菜服务员将菜端上来,你就可以吃饭了。这就是同步调用它强调方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为

那相反,你看大中午那么热不想出去吃饭,于是就叫外卖来,但选好外卖下单成功后,对你来说吃饭的问题已经解决了。虽然外卖还没送来,但是你的任务就是中午要吃饭,商家接到订单后就会加紧安排送来,但这一切都与你无关。你在等待外卖送来过程中想干啥就干啥。这就是异步调用它更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作

对于阻塞和非阻塞,同样举个例子,比如宿舍只有一个厕所,但此时舍友A正在上厕所,此时舍友B十分内急想上厕所,但没办法厕所只有一个,舍友B只能默默等待舍友A上完厕所才行。这就是阻塞它强调一个线程如果占用了临界区资源,那么其他所有需要这个资源的线程就必须在这个临界区中等待

那相反,同样是舍友A还是在上厕所,舍友B此时一样十分内急想上厕所,由于该宿舍的厕所已经被占用了,那他可以去隔壁宿舍借厕所上(这是假的其他宿舍肯定有空的厕所)。这就是阻塞它强调没有一个线程可以妨碍到其他线程执行,所有线程都会尝试不断前向执行


(2)临界区

临界区用来表示一种公共资源或者说共享数据,可以被多个线程使用。但是每一次,只能有一个线程使用它,一旦临界区资源被占用,其他线程想要使用这个资源就必须等待。

举个栗子,在办公室中只有一台打印机(一次只能打印一人的任务量),如果小明和小王想要打印一些文件,很显然,如果小王先打印文件,打印机就开始打印小王的文件,小明只能等小王打印完成后才轮到他来打印。这里的打印机就相当于一个临界区。

在高并发程序中,临界区资源是保护的对象。如果小王和小明同时使用一台打印机打印文件,那么很有可能打印出来的文件是损坏的。那么它即不是小王想要的也不是小明想要的。


(3)死锁(Deadlock)、饥饿(Stravation)、活锁(Livelock)

死锁、饥饿、活锁都属于多线程的活跃性问题。如果发现上述几种情况,那么相关线程可能就不在活跃,也就是说它很可能很难再继续往下执行

====================================================================
对于死锁,举个例子,如下图,假如现在有一条单行道,A、B小车分成从左和右驶来,由于单行道只允许车辆从某一特定方向行驶,那A、B两车中得有一个先倒回去给其中一个车让路。但如果两车都不愿意给另一方让路,那这个状态就一直持续下去,谁都无法过去。

在这里插入图片描述

====================================================================

对于饥饿,同样举了栗子。在自然界中,母鸟给雏鸟喂食时容易出现这种情况:由于雏鸟很多,但喂的食物有限,导致雏鸟之间的食物竞争十分厉害,经常抢不到食物的雏鸟有可能就会挨饿。抽象地来说,就是指某一个或多个线程因为种种原因无法获取所需要的资源,导致一直无法一直执行,线程的饥饿就类似上面这种情况。

====================================================================

对于活锁,同样举个栗子。但你要做电梯下楼时,电梯到了开门时,你准备要出去。但很不巧,电梯门外有一个人挡住了你的去路,他想进电梯。于是你很礼貌地靠右走,避让对方。很巧的时,对方也很礼貌地靠左走。结果你们俩又撞上了。于是乎,你们都意识到了问题,希望尽快避让对方,你立即向左边走同时他向右边走。结果,还是撞上了!。不过介于人类会思考,相信这个动作重复个几次后就可以解决了。

但这种情况发生在两个线程之间就不会有那么幸运了。如果线程的智力不够且都秉承“谦让”原则,主动将资源释放给别人使用,那么就会导致资源不到地在两个线程之间跳动,而没有一个线程可以同时拿到所有资源正常执行。这种情况就是活锁。

====================================================================

并发级别

根据控制并发的策略不同,我们可以将并发级别分成阻塞、无饥饿、无障碍、无锁、无等待几种


(1)阻塞(Blocking)

一个线程是阻塞的,那么在其他线程释放资源之前,当前线程无法继续执行。
当使用synchronized关键字,或重入锁时,得到的就是阻塞的线程。


(2)无饥饿(Starvation-Free)

如果线程之间是有优先级的,那么线程调度的时候总是会倾向于先优先满足高优先级的线程。对于的就会出现公平锁和非公平锁。

  • 公平锁:先来后到。所有线程都有机会执行。
  • 非公平锁:允许高优先级的线程插队。这样有可能导致低优先级线程饥饿。

(3)无障碍(Obstruction-Free)

一种最弱非阻塞调度。

2个线程如果无障碍执行,那么都可以进入临界区,但是当无障碍线程检测到有其他线程修改临界区资源时,会立即对自己所做对修改进行回滚,确保数据安全;如果没有数据竞争发生,就可以顺利完成任务,走出临界区。

但是这种调度并不一定能顺畅运行。当临界区中存在严重冲突时,所有线程可能会不断回滚自己的操作,从而没有一个线程能走出临界区。

阻塞:悲观策略
非阻塞:乐观策略。


(4)无锁(Lock-Free)

无锁并行都是无障碍的。

这种情况下,所有线程都能尝试对临界区进行访问,但是无锁对并发保证必然有1个线程能够在有限步内完成操作离开临界区。

无锁调用典型特点可能会包含一个无限循环。循环中不断尝试修改共享变量。如果没有冲突,修改成功,程序退出,否则继续尝试修改。

但是无论如何,无锁的并行总能保证有一个线程是可以胜出的,不至于全军覆没。不过对于总是竞争失败的线程,会出现类似饥饿的现象。


(5)无等待(Wait-Free)

  1. 无等待在无锁的基础上更进一步扩展
  2. 要求所有线程必须在有限步内完成

如果限制这个步骤上限,还可以进一步分解为有界无等待线程数无关的无等待几种,区别只是对循环次数限制不同。

典型结构RCU(Read-Copy-Update)。基本思想:对读不加控制,写数据时,先取得原始数据副本,只修改副本,修改完成后,在合适时机回写数据。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值