java有像syncronized这样的内置锁,但为什么还需要lock这样的外置锁? 性能并不是选择syncronized或者lock的原因,jdk6中syncronized的性能已经与lock相差不大。 如果要选择lock的话,会基于lock拥有的几个优点(内置锁所不具备): 1.如果希望当获取锁时,有一个等待时间,不会无限期等待下去。 2.希望当获取不到锁时,能够响应中断 3.当读多,写少的应用时,希望提高性能 4.获取不到锁时,立即返回false。获取到锁时返回true。 lock接口定义以下方法: public interface Lock { void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition(); } 其中lockInterruptibly(),表明加锁时,当前拥有这个锁的线程可被中断。 tryLock()则用于尝试获取锁,能获取返回true,否则返回false。 tryLock(long time, TimeUnit unit),与tryLock类似,只是会尝试一定的时间后再根据是否能够获取锁返回相应的true或false。 unlock()用于拥有锁的线程释放锁。 newCondition()方法之后介绍。 有些操作需要满足一些前提条件才能进行,这就涉及状态并发的控制。 如一个有界缓存,对于存放操作需要判断当前缓存是否满了,满了的话需要阻塞等待。不满则放入数据,并唤醒等待取数据的线程。 对于取操作,需要判断当前缓存是否非空,为空则阻塞等待。不为空则取出数据,并唤醒阻塞的进行存放操作的线程。 考虑,这样的一个有界缓存如何设计? 首先对于存放数据的数据结构可以是数组或者是一个链表。 这里我们假设选择数组。 然后定义两个操作方法,一个是存放数据到缓存的方法put,一个是取数据的方法take。 还需要一个int型的count代表当前已有元素数量,int型的header用于指向当前要取元素的位置,一个tail用于指向当前存放元素的位置。 接着关键是要保证put与take在并发的情况下,保证数据操作完整性,不出现异常行为。 这就需要保证并发调用put操作时是加锁互斥的,否则会发生以下情况: 当前缓存数组大小为3,当前已经在缓存的数据有两个。 这时线程一进行以下存放步骤操作: 1.线程一首先判断当前数组是否未满 2.这时未满接着线程一往缓存存数据 但当线程一进行第二步操作:往缓存存数据时,线程二提前将数据放入缓存,这时数组大小为3 这样线程一再往数组放数据时,就超出数组长度了。 所以put操作必需同步控制。 其次,由于需要保存count当前元素数量,因此也需要保证存取操作put及take方法互斥。 简单实现上述buffer代码如下:
分析这个程序,有什么问题?
接下去的任务是搞清楚,lock内部实现原理,整个实现主要组件组成,学习其中的一些优秀想法,设计。
|
|