《Java并发编程的艺术》第一章 Java 并发编程的挑战

第一章 Java 并发编程的挑战

说明并发编程的世界中可能遇到的哪些问题,以及如何解决。

并发的目的是让程序运行得更快,但并不是更多的线程就能让程序最大限度地并发执行。在多线程情景下,面临诸多挑战,如上下文切换、死锁、受限于软硬件资源等问题。

1.1 上下文切换

Cpu 通过为每个线程分配 cpu 时间片来实现多线程执行代码,因为时间片非常的短,所以cpu 需要通过不停地切换线程执行,让我们感觉多个线程是在同时执行的。

但是,在切换之前cpu会保存上一个任务的状态,以便下次切换回任务时,会加载任务的状态。

所以任务从保存到再加载的过程就是一次上下文切换。

很明显,当程序中的多个线程存在大量的上下文切换,程序执行的速度未必会比串行来得快。

1.1.1 减少上下文切换

  • 无锁并发编程。多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以通过将数据的ID 按照 Hash算法取模分段,不同线程处理不同段的数据的方式,避免使用锁。
  • CAS 算法。Java 的 Atomic 包使用CAS 算法来更新数据,不需要加锁
  • 使用最少线程。避免创建不需要的线程,比如任务很少,但是创建了很多线程来处理,这样会造成大量线程都处于等待状态
  • 协程。在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。

1.2 死锁

死锁,即多个线程因某些问题无法释放锁,导致多个线程之间互相等待的场景。

避免死锁的常见方法:

  • 避免一个线程同时获取多个锁
  • 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源
  • 尝试使用定时锁,使用 lock.tryLock(timeout) 来替代使用内部锁机制
  • 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。

1.3 资源限制的挑战

资源限制是指程序受限于软硬件的条件,而不能达到预期的处理效果。主要包含有:带宽的上传/下载速度、硬盘读写速度、CPU处理速度。数据库连接数、socket连接数等。

引发的问题:并发执行时,因为增加了上下文切换和资源调度时间的原因。程序运行时可能会更慢。

解决方法:使用ODPS、Hadoop或者搭建服务器集群,不同机器处理不同的数据。可以通过 “数据ID%机器数”,计算出一个机器编号,然后由对应编号的机器处理这笔数据。

在资源受限的情况下进行并发编程:需要根据不同的资源限制调整程序的并发度。

文章作者: koral
文章链接: http://luokaiii.github.io/2019/06/14/读书笔记/《Java并发编程的艺术》/1.并发编程的挑战/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自