一.初识Java并发编程
一.什么是并发编程
在程序中,有很多耗时但又不占用CPU的任务(比如输入输出),如果采用用串行的方式运行程序,那么CPU绝大部分的时间都是空闲的.CPU通过给每个线程分配时间片,让各个线程间断执行,以此来充分利用CPU.因为时间片非常短,让我们感觉这些任务是同时执行的,所以叫并发执行.(需要注意,这里的并发实际上每个瞬间只有一个线程在运行)
并发编程是为了提高CPU效率而设计的,那么并发编程一定快么?
上图是一个累加程序运行时间分析,由表可知,当累加的次数大于1百万时,并发的效率明显高于串行执行,反则亦然.这是因为,使用并发编程时,需要考虑到线程切换的成本:上下文切换.
二.上下文切换
2.1什么是上下文切换
上下文切换其实就是工作环境的切换,其主要步骤如下:
- 将前一个CPU的上下文保存起来
- 加载新任务的上下文到寄存器和程序计数器(CPU寄存器是CPU内置容量小但是速度极快的内存,程序计数器是正在执行或即将执行的指令位置)
- 跳转到程序计数器所在的代码,开始执行
2.2如何减少上下文切换
线程发生上下文切换时主要发生在如下两种情况中:
- 线程运行的时间片时间用完了,线程切换。
- 若线程在时间片结束前阻塞或结束,线程切换。
所以,基于以上两点,提出如下优化方案:
- 无锁并发编程,多线程竞争锁时,可能会因为获取不到锁而阻塞,产生如上条件2而进行上下文切换,通过无锁并发编程,比如对任数据ID进行hash,不同线程处理不同数据段,以此来减少因为线程阻塞而带来的上下文切换。
- CAS算法,通过CAS算法,可以在不加锁的情况下,循环等待来更新数据,以此来减少上下文的切换。
- 使用最少线程,避免创建不必要的线程,不然,很多处于等待状态。
- 使用协程,在单线程里实现多任务调度,并在单线程里维护多个任务间的切换。
三.死锁对并发编程的影响
多线程能够极大地提高程序运行的效率,但是如果不正确的使用多线程,难免会带来灾难性的错误,其中最常见的就是死锁。
所谓死锁就是线程1拥有A锁,等待B锁,线程2拥有B锁等待A锁,那么出现这种彼此等待的情况会导致线程举步不前,还会耗费大量的资源。
如何避免成为一个写BUG程序员,对于死锁,可注意一下几点:
- 避免一个线程获得多个锁
- 避免一个线程在锁内占有多个资源
- 定时锁的使用
- 对于数据库 加锁何解锁必须在一个数据库连接里
四.资源限制对并发编程的影响
如果服务器的带宽只有2M/s,某个资源的下载速度时1M/s,那么开启10个线程不会让下载速度达到10M/s,相反,它可能会因为上下文切换而小于2M/s,所以,使用多线程时,还需要考虑资源的限制。
资源限制主要考虑硬件资源和软件资源的影响,对于硬件资源,可以考虑用集群并行执行程序,对于软件资源,可以考虑使用资源池将资源复用。
看完这篇文章,你学废了以下内容么:
1.什么是并发编程?并发编程一定快么?
2.什么是上下文切换?如何减少上下文切换?
3.死锁对并发编程的影响,如何避免死锁的出现?
4.资源限制对并发编程的影响,如何改善资源的瓶颈?
看完这篇文章,你学废了以下内容么:
1.什么是并发编程?并发编程一定快么?
2.什么是上下文切换?如何减少上下文切换?
3.死锁对并发编程的影响,如何避免死锁的出现?
4.资源限制对并发编程的影响,如何改善资源的瓶颈?
注:本文总结于Java并发编程的艺术和个人所学