Home Java并发编程的艺术(笔记) 第一章 并发编程的概念及挑战(下—挑战)

第一章 并发编程的概念及挑战(下—挑战)

技术并无高低,只有是否适用。并发编程也是如此,在某些场景下,使用并发的策略会提高效率,但另一些情况下使用并发反而会较低效率;同时,并发虽好,使用时可不要对其掉以轻心

因为并发往往会带来许多挑战

1.上下文切换

并发是一种策略:将某任务拆分,让多个线程去实现(狭义理解),正因如此,单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现。由于需要进行进程切换,在切换前,我们会保存该线程的状态,等到下次该线程得到时间片的时候方便加载,那么从保存到加载就称之为一次上下文切换。上下文切换需要消耗资源!

解决办法:减少上下文切换

  • 无锁并发编程(分段锁—ConCurrentHashMap)
  • CAS算法(Atomic类,无需加锁)
  • 使用最少线程(避免创建不必要的线程)
  • 使用协程(单线程实现多任务调度,维持多个任务间的切换)

2.死锁

线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。

当线程进入对象的synchronized代码块时,便占有了资源,直到它退出该代码块或者调用wait方法,才释放资源,在此期间,其他线程将不能进入该代码块。

当线程互相持有对方所需要的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁。

死锁产生的原因:

1) 系统资源的竞争

2) 进程推进顺序非法

死锁产生的必要条件:产生死锁必须同时满足以下四个条件,只要其中任一条件不成立,死锁就不会发生。

1)  资源互斥条件(如打印机)

2) 不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走

3) 请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放

4) 循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被 链中下一个进程所请求

解决办法:破坏其必要条件(四个中的一个)

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

3.资源限制

什么是资源限制?

资源限制是指在进行并发编程时,程序的执行速度受限于计算机硬件资源或软件资源。 例如,服务器的带宽只有2Mb/s,某个资源的下载速度是1Mb/s每秒,系统启动10个线程下载资 源,下载速度不会变成10Mb/s,所以在进行并发编程时,要考虑这些资源的限制。硬件资源限 制有带宽的上传/下载速度、硬盘读写速度和CPU的处理速度。软件资源限制有数据库的连接 数和socket连接数等。

资源限制引发的问题

在并发编程中,将代码执行速度加快的原则是将代码中串行执行的部分变成并发执行,但是如果将某段串行的代码并发执行,因为受限于资源,仍然在串行执行,这时候程序不仅不会加快执行,反而会更慢,因为增加了上下文切换和资源调度的时间。例如,之前看到一段程 序使用多线程在办公网并发地下载和处理数据时,导致CPU利用率达到100%,几个小时都不
能运行完成任务,后来修改成单线程,一个小时就执行完成了。

如何解决资源限制的问题(硬件集群,软件复用)

对于硬件资源限制,可以考虑使用集群并行执行程序。既然单机的资源有限制,那么就让 程序在多机上运行。

对于软件资源限制,可以考虑使用资源池将资源复用。比如使用连接池将数据库和Socket 连接复用,或者在调用对方webservice接口获取数据时,只建立一个连接。

在资源限制情况下进行并发编程(因地适宜)

如何在资源限制的情况下,让程序执行得更快呢?方法就是,根据不同的资源限制调整 程序的并发度,比如下载文件程序依赖于两个资源——带宽和硬盘读写速度。有数据库操作 时,涉及数据库连接数,如果SQL语句执行非常快,而线程的数量比数据库连接数大很多,则某些线程会被阻塞,等待数据库连接

发表评论

发表评论