走入并行的世界

1.忘掉那些该死的并行

2014年底,Linus Torvalds 提出了一个观点:"忘掉那该死的并行吧!"他指出并行计算只有在图像处理和服务器端2个领域可以使用,并且它在这两个领域确实有着大量广泛的应用,但是在其他方面,并行计算毫无建树的。与串行程序相比,并行程序的设计和实际异常复杂,不仅仅体现在程序的功能分离上,多线程之间的协调性和乱序性都回成为程序正确执行的障碍。

2.不断的前进

继摩尔定律失效之后,从2015年开始,我们已不再追求单核的计算速度,而是着迷于研究如何将多个独立的计算单元整合到单独的CPU中,也就是我们所说的多核。所以,并行计算显然已经成为一门正式的学问,先熟悉一下几个概念。

同步(Synchronous)和异步(Asynchronous)

同步和异步通常来形容一次方法的调用。同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。异步方法调用向一个消息传递,一旦开始就会立即返回,调用者就可以进行后续的操作。

并发(Concurrency)和并行(Parallelism)

并行是真正意义上的同时执行,而并发是几个任务之间的自由切换。

临界区

临界区表示一种公共资源或者说是共享数据,可以被多个线程使用。但是每一次,只能有一个线程使用它,一旦临界区被占用了,其他线程要想使用这个资源,就必须等到。举个例子:打印机一次只能执行一个任务。

阻塞(Blocking)和非阻塞(Non-Bloking)

阻塞和非阻塞都是用来形容多线程之间的相互影响。阻塞是一个线程占用了资源,其他线程就必须挂起等待。非阻塞就是强调没有一个线程可以妨碍其他线程执行。

死锁(Deadlock),饥饿(Starvation)和活锁(Livelock)

死锁是非常严重的一种情况,就是每个线程都占着不同的资源,不释放,那么其他的资源就无法再进行调用,应该避免和时时小心这种情况。

饥饿是指一个或多个线程因为种种原因无法获得所需要的资源,导致一直无法执行。例如线程优先级可能太低,而高优先级的线程不断抢占它需要的资源,导致低优先级的一直没有办法工作,与死锁相比,饥饿还是可以在未来一段时间内解决的。

活锁是假设线程智力不够,而且都很谦让,主动将资源释放给别人使用,这样就会出现资源不断的在2个线程之间跳动,而没有一个线程可以同时拿到所有资源来正常执行。

3.并发级别

根据控制并发的策略,大致上可以分为阻塞,无饥饿,无障碍,无锁,无等待

阻塞:当我们使用synchronized关键字或者重入锁的时候,我们就得到了阻塞的线程。

无饥饿:这里有2个概念,公平锁和非公平锁。公平锁就是所有的线程都满足先来后到,即便后来的线程优先级高,也得等待前面的线程执行完之后再执行。非公平锁是优先级高的线程可以插队,这样可能导致低优先级的线程产生饥饿。

无障碍:这是一种最弱的非阻塞调用,2个线程如果是无障碍执行,那么就不会因为临界区的问题导致一方挂起,大家就可以一起修改共享数据,一旦发现数据坏了,它就会对自己所做的修改进行回滚,确保数据的安全。一种可行的无障碍实现可以依赖一个"一致性标记"来实现,线程在操作之前先读取并保存这个标记,在操作完以后,再次读取,检查这个标记是否被改过,如果是一致的,就说明资源访问没有冲突,如果不一致,说明资源在操作过程中与其他线程冲突,要重新操作。

无锁:无锁的并行都是无障碍的,在这种情况下,所有的线程都尝试对临界区的资源进行访问,但是保证必然有一个线程能够在有限时间内完成操作并且离开临界区。

无等待:这种并行要求所有的线程都要在有限的步骤内完成,这样就不会引起饥饿。一种典型的无等待结构是RCU(Read-Copy-Update)它的基本思想是所有读线程都是无等待的,他们既不会被锁定也不会引起冲突,但是在写数据的时候,需要先取得原始数据的副本,只修改副本,修改完之后,在适当的时候回写数据。

 

 

该文章是对<实战JAVA高并发程序设计>第一章的粗略总结

 

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值