java并发编程
并发编程的挑战
并发编程的目的是为了让程序运行的更快,但是运行更多的线程并不能让程序最大限度的并发执行,还会面邻以下挑战。
上下文切换
即使是单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制也就是时间片轮转机制。
时间片是CPU分配给各个线程的时间,因为时间片非常短,所以CPU通过不停地切换线程执行,让我们感觉多个线程是同时执行的,时间片一般是几十毫秒。
任务从保存到再加载的过程就是一次上下文切换。
过多的线程创建和上下文切换会造成额外的开销,也会影响程序的运行效率。
-
减少上下文切换的方法
-
无锁并发编程
多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一些办法来避免使用锁。
如将数据的ID按照Hash算法取模分段,不同的线程处理不同段的数据。 -
CAS算法
Java的Atomic包使用CAS算法来更新数据,而不需要加锁。 -
使用最少线程
避免创建不需要的线程,比如任务很少,但是创建了很多线程来处理,这样会造成大量线程都处于等待状态。 -
使用协程(腾讯、字节跳动问的较多,java中没有)
在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。
-
死锁
一旦产生死锁,就会造成系统功能不可用
-
死锁产生的必要条件
- 互斥 对于某个变量的互斥访问
- 请求与保持 请求完变量之后还需要其他变量,并且不释放拥有的的变量
- 不可剥夺 线程不能剥夺其他线程拥有的变量
- 循环等待
-
避免死锁的几个常见方法
- 避免一个线程同时获取多个锁 (破坏请求保持)
- 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源(破坏请求保持)
- 尝试使用定时锁,使用lock.tryLock来代替使用内部锁机制 (破坏请求保持、循环等待)
- 对于数据库锁,加锁和解锁必须在一个数据库链接里,否则会出现解锁失败的情况
资源限制的挑战
- 什么是资源限制
资源限制是指在进行并发编程时,程序的执行速度受限于计算机硬件资源或软件资源。
例如,服务器的带宽只有2Mb/s,某个资源的下载速度是1Mb/s每秒,系统启动10个线程下载资源,下载速度不会变成10Mb/s,所以在进行并发编程时,要考虑这些资源的限制。
- 资源限制引发的问题
在并发编程中,将代码执行速度加快的原则是将代码中串行执行的部分变成并发执行,但是如果将某段串行的代码并发执行,因为受限于资源,仍然在串行执行,这时候程序不仅不会加快执行,反而会更慢,因为增加了上下文切换和资源调度的时间。
- 如何解决资源限制的问题
对于硬件资源限制,可以考虑使用集群并行执行程序。既然单机的资源有限制,那么就让
程序在多机上运行。比如使用ODPS、Hadoop或者自己搭建服务器集群,不同的机器处理不同
的数据。
- 在资源限制情况下进行并发编程
根据不同的资源限制调整程序的并发度