重入锁的好搭档:Condition条件(读书笔记)
Condition结构提供了基本方法如下:
void await() throws InterruptedException; void awaitUninterruptibly(); long awaitNanos(long nanosTimeout) throws InterruptedException; boolean await(long time, TimeUnit unit) throws InterruptedException; boolean awaitUntil(Date deadline) throws InterruptedException; void signal(); void signalAll();
- await()方法会使当前线程等待,同时释放当前锁,当其他线程中使用singal()和signalAll()方法时,线程会重新获得锁并继续执行.或者当前线程中断时,也跳出等待.
- awaitUninterruptibly()方法与await()方法基本相同,,但是它并不会在等待过程中中断响应.
- singal()方法用于唤醒一个在等待的线程,
public class ReenterLockCondition implements Runnable { public static ReentrantLock lock = new ReentrantLock(); public static Condition condition = lock.newCondition(); /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see Thread#run() */ @Override public void run() { try { lock.lock(); condition.await(); System.out.println("Thread is going on"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public static void main(String[] args) throws InterruptedException { ReenterLockCondition r1 = new ReenterLockCondition(); Thread t1 = new Thread(r1); t1.start(); Thread.sleep(2000); lock.lock(); condition.signal(); lock.unlock(); } }
当线程使用Condition.await()时,要求线程持有相关的重入锁,在Condition.await()调用后,这个线程会释放这把锁,同理 在Condition.signal()方法调用时,也要求线程先获得相关的锁,在signal()方法调用后,西永会从当前的Condition对象的等待队列中,唤醒一个线程,.一旦线程被唤醒,他会重新尝试获得与之绑定的重入锁,一旦成功获取,就可以继续执行,因此,在signal()方法调用后,一般需要释放相关的锁.
put()的实现如下:
public void put(E e) throws InterruptedException { if (e == null) throw new NullPointerException(); // Note: convention in all put/take/etc is to preset local var // holding count negative to indicate failure unless set. int c = -1; Node<E> node = new Node<E>(e); final ReentrantLock putLock = this.putLock; final AtomicInteger count = this.count; putLock.lockInterruptibly();//可以响应中断的锁 加锁 try { /* * Note that count is used in wait guard even though it is * not protected by lock. This works because count can * only decrease at this point (all other puts are shut * out by lock), and we (or some other waiting put) are * signalled if it ever changes from capacity. Similarly * for all other uses of count in other wait guards. */ while (count.get() == capacity) {//判断队列是否满了 notFull.await();//等待 } enqueue(node);//插入数据 c = count.getAndIncrement();//更新总数,变量c是count加1前的值 if (c + 1 < capacity) notFull.signal();//有足够的空间,通知其他线程 } finally { putLock.unlock();//释放锁 } if (c == 0) signalNotEmpty();//插入成功后,通知take操作去数据 }
通过takeLock和putLock两把锁,LinkedBlockingQueue实现了取数据和写数据的分离,使两者在真正意义上成为可以并发的操作
- 锁粗化,在开发过程中,应该有意识地在合理的场合进行锁的粗化,尤其在循环内请求锁时,如果对一个锁不停地进行请求,同步和释放,其本身也会消耗宝贵的资源,反而不利于性能的优化,
性能的优化是根据运行时的真实情况对各个资源点进行权衡折中的过程,锁粗化的思想和减少锁的持有时间是相反的,但在不同的场合,他们的效果并不相同.所有大家需要根据实际情况,进行权衡.