线程安全
在多线程条件下,共享状态能够保持正确性。
Synchronized
- 反编译的时候,加入了monitorenter和monitorexit指令,实现了同步的语义。
- monitor是同步的基本组成单元。以前是依靠操作系统的内部锁,在用户态和核心态中转化,是一个重量级的操作。
- 现在的JDK对Monitor做了很多的改进,进行了三种实现:
- 没有竞争时,默认使用偏斜锁。JVM会利用CAS操作,在对象头里的mark word字段里写入这个线程的ID,表示对象偏向于这个线程。并不涉及真正的互斥锁。
- 如果有其他的线程试图获取某个已经被偏斜过的对象,JVM需要撤消偏斜锁,并切换到轻量级的锁实现。同样利用CAS更改mark word来试图获取锁。
ReentrantLock
- 一个线程获取它已经获取的锁时,能够自动成功。这是一个对锁粒度的概念,锁的持有是以线程为单位而不是调用的次数。
- 比前者更有定制性,可以响应中断请求,可以进行超时的锁获取。
- 并发包里的Condition条件变量,将对线程的wait,notify操作转化为对对象的操作,将同步操作转化为对象行为。
ArrayBlockQueue
在使用ReentrantLock类的时,一定要注意三点:
在finally中释放锁,目的是保证在获取锁之后,最终能够被释放
不要将获取锁的过程写在try块内,因为如果在获取锁时发生了异常,异常抛出的同时,也会导致锁无故被释放。
ReentrantLock提供了一个newCondition的方法,以便用户在同一锁的情况下可以根据不同的情况执行等待或唤醒的动作。
f5753a4695fd771f8178120858086811.png
死锁
所以,从程序设计的角度反思,如果我们赋予一段程序太多的职责,出现“既要…又要…”的情况时,可能就需要我们审视下设计思路或目的是否合理了。对于类库,因为其基础、共享的定位,比应用开发往往更加令人苦恼,需要仔细斟酌之间的平衡。
- 如果可能的话,尽量避免使用多个锁,并且只有需要时才持有锁。否则,即使是非常精通并发编程的工程师,也难免会掉进坑里,嵌套的 synchronized 或者 lock 非常容易出问题。
- 如果必须使用多个锁,尽量设计好锁的获取顺序,这个说起来简单,做起来可不容易,你可以参看著名的银行家算法
- 使用带超时的方法,为程序带来更多可控性。
- 静态代码分析
网友评论