四、线程同步之Lock和Condition

时间:2022-06-01 22:00:24

Lock同步锁

Lock
在jdk1.5  提供了Lock以便执行同步操作,和synchronized不同的是Lock提供了显示的方法获取锁和释放锁。Lock提供了以下几个方法,请求和释放锁:
  • void lock()  
    获取锁,当前锁若是不可用的时候,此线程将休眠直到锁被获取到。
  • void lockInterruptibly()
    获取锁,当前锁若是不可用,此线程将休眠直到锁被获取。有两种情况线程不会休眠:
    1、当前线程获取了锁
    2、其他线程终止了当前线程,并且终止获取锁是被允许的。
  • boolean tryLock()
    获取锁,当前锁可用则返回'true',反之返回'false';使用方法如下:
    1. Lock lock = ...;
    2. if (lock.tryLock()) {
    3. try {
    4. // manipulate protected state
    5. } finally {
    6. lock.unlock();
    7. }
    8. } else {
    9. // perform alternative actions
    10. }
  • boolean tryLock(long,TimeUnit)
    获取锁,在规定的时间内,锁一直不可用返回'false',反之返回'true'
  • void unlock()
    释放锁。使用事项:此方法和获取锁的几个方法一一对应,获取到了锁,必须最后调用此方法释放锁。
  • Condition newCondition()
    获取一个Condition对象,操作当前锁。
使用原则 获取锁==>run()==>释放锁。这一原则和synchronized是一样的,只是获取和释放时是显示调用的,也是因为lock和unlock的组合使用,多个同步方法能很方便的嵌套使用,执行完后会依次释放自己所持有的锁。如下:
  1. Lock l = ...;
  2. l.lock();
  3. try{
  4. //do something
  5. } finally{
  6. l.unlock();
  7. }
通常锁提供了资源的独占访问,不过某些确实允许对资源的并发访问,如ReadWriteLock(读写锁),开发中常用的ReentantLock(可重入锁),比如下面使用方法:
  1. class X {
  2. private final ReentrantLock lock = new ReentrantLock();
  3. // ...
  4. public void m() {
  5. lock.lock(); // block until condition holds
  6. try {
  7. // ... method body
  8. } finally {
  9. lock.unlock()
  10. }
  11. }
  12. }

Condition
使用Lock替换了Synchronized实现同步操作时,Condition替换了Object 监视器方法的使用。
Conditions (也可以称为条件队列或者条件变量)为某些行为提供了用途,如一个线程需要等待,直到某些条件为ture,被其他线程唤醒。因为在不同线程里访问共享信息是必须受到保护,因此锁的状态根据某些条件决定的。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。
一个锁绑定了一个Condition对象。可以通过Lock的newCondition()方法获取。下面写了一个小例子:
  1. class BoundedBuffer {
  2. final Lock lock = new ReentrantLock();
  3. final Condition notFull = lock.newCondition();
  4. final Condition notEmpty = lock.newCondition();
  5. final Object[] items = new Object[100];
  6. int putptr, takeptr, count;
  7. public void put(Object x) throws InterruptedException {
  8. lock.lock();
  9. try {
  10. while (count == items.length)
  11. notFull.await();
  12. items[putptr] = x;
  13. if (++putptr == items.length) putptr = 0;
  14. ++count;
  15. notEmpty.signal();
  16. } finally {
  17. lock.unlock();
  18. }
  19. }
  20. public Object take() throws InterruptedException {
  21. lock.lock();
  22. try {
  23. while (count == 0)
  24. notEmpty.await();
  25. Object x = items[takeptr];
  26. if (++takeptr == items.length) takeptr = 0;
  27. --count;
  28. notFull.signal();
  29. return x;
  30. } finally {
  31. lock.unlock();
  32. }
  33. }
  34. }
方法描述:
Modifier and Type Method and Description
void await()
使当前线程处于等待状态,直到收到信号或者被打断.
boolean await(long time, TimeUnit unit)
在规定时间里使当前线程处于等待状态,直到在这段时间内收到信号或者被打断。。
long awaitNanos(long nanosTimeout)
在规定时间里使当前线程处于等待状态,直到在这段时间内收到信号或者被打断。
void awaitUninterruptibly()
使当前线程处于等待状态明知道收到信号。
boolean awaitUntil(Date deadline)
在规定时间里使当前线程处于等待状态,直到在这段时间内收到信号或者被打断。.
void signal()
唤醒一个正在等待的线程.
void signalAll()

补充
ReentrantLock 锁具有重入性,也就是说线程可以对它已经加锁的ReentrantLock锁再次加锁,ReentrantLock对象会维持一个计数器来跟踪lock方法的嵌套调用,线程在每次调用lock()加锁后,必须显式调用unlock()来释放锁,所以一段被锁保护的代码可以调用另一个被相同锁保护的方法。