死锁、活锁、性能问题

活跃性危险

死锁

什么是死锁?

一条小路上两个人堵到了一起,a说你让我先走吧,b说不行我不让,你先让我走,互不相让,两个人都过不去。

A线程等待b线程的锁,b线程的任务在等待a线程的锁,两个线程在此处不停尝试获得锁,永无休止。

1.png

死锁的种类:

静态锁顺序死锁

2.png

如图直接看代码就能看出有死锁问题。

动态锁顺序死锁

代码看起来好好的,只有运行时在某种特定的极端情况下才会出现。

3.png

当fromAccount和toAccount同时相互转账并且都进入了第一层synchronized开始尝试获取第二层synchronized的锁时就会发生死锁。

资源死锁

多个线程加锁访问共同资源。

饥饿死锁:

线程一直等不到需要的资源,比如调接口、访问数据库时一直得不到响应。

防止死锁的方式:

1.显示锁的定时tryLock方法,尝试获取锁超时后就放弃获取锁操作,避免永久尝试。

2.调整程序逻辑,避免死锁。

活锁

一个相同的任务执行一次后不能完成,然后反反复复执行重演历史,始终无法完成。

性能和伸缩性

性能:用尽可能少的资源尽可能短的时间做尽可能多的操作。

伸缩性:吞吐量和处理能力不够用的时候,只要加机器,吞吐量和处理能力就能提高。

多线程会带来哪些开销?

1.上下文切换

2.内存同步

Synchronized和volatile提供的内存可见性会造成一些内存开销,可能会使用到内存栅栏(又称内存屏障memory barrier)指令,常常会刷新cpu的缓存、抑制编译器优化操作(例如防止对这一部分代码重排序、自动去掉一些不必要的锁),主要就是会访问主内存并同步线程的cpu缓存。

4.png

3.阻塞

独占锁是性能和伸缩性主要威胁。

威胁度=锁的请求频率x每次占用锁的时间

威胁度越低,线程对锁的竞争度就越低。

降低锁的竞争度的方法:

1.减少请求锁的频率

2.减少占用锁的时间

被锁保护的代码应尽可能短小,不要执行长时间任务,不需要锁保护的代码统统拿出去,锁的粒度越小越好(能用对象锁就不要用类锁,能用局部代码块就不要用方法锁),快进快出。

3.使用带有协调机制的独占锁

能提供更高的并发。(比如分段锁、读写锁)

非阻塞同步

posted @ 2019-09-21 19:10  纸飞机上的梦  阅读(320)  评论(0编辑  收藏  举报