死锁、活锁、性能问题
活跃性危险
死锁
什么是死锁?
一条小路上两个人堵到了一起,a说你让我先走吧,b说不行我不让,你先让我走,互不相让,两个人都过不去。
A线程等待b线程的锁,b线程的任务在等待a线程的锁,两个线程在此处不停尝试获得锁,永无休止。
死锁的种类:
静态锁顺序死锁
如图直接看代码就能看出有死锁问题。
动态锁顺序死锁
代码看起来好好的,只有运行时在某种特定的极端情况下才会出现。
当fromAccount和toAccount同时相互转账并且都进入了第一层synchronized开始尝试获取第二层synchronized的锁时就会发生死锁。
资源死锁
多个线程加锁访问共同资源。
饥饿死锁:
线程一直等不到需要的资源,比如调接口、访问数据库时一直得不到响应。
防止死锁的方式:
1.显示锁的定时tryLock方法,尝试获取锁超时后就放弃获取锁操作,避免永久尝试。
2.调整程序逻辑,避免死锁。
活锁
一个相同的任务执行一次后不能完成,然后反反复复执行重演历史,始终无法完成。
性能和伸缩性
性能:用尽可能少的资源尽可能短的时间做尽可能多的操作。
伸缩性:吞吐量和处理能力不够用的时候,只要加机器,吞吐量和处理能力就能提高。
多线程会带来哪些开销?
1.上下文切换
2.内存同步
Synchronized和volatile提供的内存可见性会造成一些内存开销,可能会使用到内存栅栏(又称内存屏障memory barrier)指令,常常会刷新cpu的缓存、抑制编译器优化操作(例如防止对这一部分代码重排序、自动去掉一些不必要的锁),主要就是会访问主内存并同步线程的cpu缓存。
3.阻塞
独占锁是性能和伸缩性主要威胁。
威胁度=锁的请求频率x每次占用锁的时间
威胁度越低,线程对锁的竞争度就越低。
降低锁的竞争度的方法:
1.减少请求锁的频率
2.减少占用锁的时间
被锁保护的代码应尽可能短小,不要执行长时间任务,不需要锁保护的代码统统拿出去,锁的粒度越小越好(能用对象锁就不要用类锁,能用局部代码块就不要用方法锁),快进快出。
3.使用带有协调机制的独占锁
能提供更高的并发。(比如分段锁、读写锁)
非阻塞同步