JAVA并发概要
什么是JAVA并发
java并发提高程序的执行速度,然而并不是多线程就一定比单线程高效,而且并发容易出错;若要实现正确并且高效的并发,那么要注意三个问题:上下文切换、死锁、资源限制。
上下文切换
上下文切换:当一条线程的时间片使用完成后,操作系统暂停线程该线程,保存他的状态信息,然后操作系统从线程堆中随机选取一个线程来执行,这个过程,称为上下文切换。
但是:上下文切换都是有额外的开销的;
线程的运行机制
- 一个Cpu在每个时刻只能执行一条线程
- 操作系统给每个线程分配不同长度的时间片
- 操作系统从线程堆中随机选取一条来执行
每条线程用完他的时间片后,尽管任务还没有完成,操作系统也会剥夺他的执行权,让其他的线程执行。
上下文切换的过程
- 暂停正在运行的线程。
- 保存其相关信息
- 从队列中随机选取线程
- 读取线程的上下文信息,继续执行
上下文切换是有额外的开销的
每次上下文切换都需要保存当前线程的执行状态,并加载新的线程的先前的状态。如果上下文切换频繁,CPU在上下文切换的时间占比就会上升,然而真正处理的任务时间就会下降。故:为了提高并发程序的执行效率,需要减少线程的数量。
上下文切换的方式
- 减少线程的数量。
- 控制同一锁上的线程数量。如果多个线程公用同一把锁,那么当其中的一个线程获得锁后,其他的线程就会被阻塞掉;当该线程释放锁后,操作系统就会用被阻塞的进程中选取一个来执行,从而造成上下文的切换。控制同一把锁下面的线程数量,可以提高并发程序的执行效率。
- 采用无锁开发编程。从②可得,如果不用锁的话,那么就不会产生阻塞就不会出现上下文切换。实现无锁并发编程有两种策略:①需要并发执行的任务是无状态的:HASH分段;无状态指的是在并发执行的任务之间没有共享变量,他们都是独立执行的,对其按照其ID进行HASH分段,每一段都会有一个线程执行。②需要并发执行的任务是由状态的:CAS算法;如果任务需要修改共享变量,那么必须控制线程的执行顺序,否则会出现安全问题,也可以给任务加锁,保证其原子性和可见性,但是线程执行会引发阻塞,从而出现上下文切换,使用CAS算法,在线程的内部完成共享变量的更新,就不会引发阻塞,那么就不会出现上下文切换。
死锁
当多个线程相互等待已经被对方占用资源时,就会发现死锁。
怎么避免死锁
- 不要在同一个线程中嵌套使用多个锁。
- 不要在同一条线程中嵌套占用多个计算机资源。
- 如果坚持在同一个线程中嵌套使用多个锁或者占用多个计算机资源,那么需要给锁加一个超时时间,来避免相互等待的情况。
计算机的资源会限制并发
并不是线程越多速度越快
在并发编程中,有时候线程多了反而效率会低下;①线程增多导致上下文切换增加,Cpu在上下文执行的时间增多,那么相应的处理时间就减少了。②计算机资源会限制程序的并发程序。
重排序
重排序指编译器和处理器不改变运算结果的情况下,重新排列指令的执行顺序,达到最大的执行效率。
重排序分类:编译器排序和处理器排序
注意:编译器排序和处理器排序,并不会随意的改变指令的执行顺序,因为有些指令具有依赖关系的,若改变其顺序就会出现错误的结果。编译器和处理器只会对没有数据依赖关系的指令排序。
数据依赖:若相邻的两个指令访问同一个变量,并且其中有一条指令执行写操作,那么这样的两条指令存在数据依赖。对有数据依赖的指令,编译器和处理器都不会对其重排序。
数据依赖有3中情况:读后写,写后写,写后读;