threadReentranLockAndCondition

来源:互联网 发布:linux 文件写权限 编辑:程序博客网 时间:2024/06/09 14:52

1   ReentrantLock

1.1 introduction

java.util.concurrent.locks
类 ReentrantLock

java.lang.Object

  java.util.concurrent.locks.ReentrantLock

所有已实现的接口:

Serializable,Lock

public class ReentrantLock

extends Object

implements Lock,Serializable

一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。

ReentrantLock 将由最近成功获得锁,并且还没有释放该锁的线程所拥有。当锁没有被另一个线程所拥有时,调用 lock 的线程将成功获取该锁并返回。如果当前线程已经拥有该锁,此方法将立即返回。可以使用isHeldByCurrentThread()getHoldCount() 方法来检查此情况是否发生。

此类的构造方法接受一个可选的公平 参数。当设置为 true 时,在多个线程的争用下,这些锁倾向于将访问权授予等待时间最长的线程。否则此锁将无法保证任何特定访问顺序。与采用默认设置(使用不公平锁)相比,使用公平锁的程序在许多线程访问时表现为很低的总体吞吐量(即速度很慢,常常极其慢),但是在获得锁和保证锁分配的均衡性时差异较小。不过要注意的是,公平锁不能保证线程调度的公平性。因此,使用公平锁的众多线程中的一员可能获得多倍的成功机会,这种情况发生在其他活动线程没有被处理并且目前并未持有锁时。还要注意的是,未定时的tryLock 方法并没有使用公平设置。因为即使其他线程正在等待,只要该锁是可用的,此方法就可以获得成功。

建议总是 立即实践,使用 lock 块来调用 try,在之前/之后的构造中,最典型的代码如下:

 class X {

   private finalReentrantLock lock = new ReentrantLock();

   // ...

 

   public void m(){

    lock.lock();  // block untilcondition holds

     try {

       // ...method body

     } finally {

      lock.unlock()

     }

   }

 }

除了实现 Lock接口,此类还定义了 isLocked 和getLockQueueLength 方法,以及一些相关的 protected 访问方法,这些方法对检测和监视可能很有用。

该类的序列化与内置锁的行为方式相同:一个反序列化的锁处于解除锁定状态,不管它被序列化时的状态是怎样的。

此锁最多支持同一个线程发起的 2147483648 个递归锁。试图超过此限制会导致由锁方法抛出的 Error

从以下版本开始: 1.5

另请参见: 序列化表格

1.2 example

public class SellTicketTask implements Runnable{

   private intticket=100;

   private Lockl=new ReentrantLock();

   public voidrun() {

      while(ticket>0){

         l.lock();

            if(ticket>0){

                System.out.println(Thread.currentThread().getName()+"--"+ticket);

                ticket--;

                try {

                   Thread.sleep(5);

                } catch (InterruptedExceptione) {

                   e.printStackTrace();

                }

            } 

         l.unlock();

      }

   }

2   Condition

2.1 introduction

java.util.concurrent.locks
接口 Condition

所有已知实现类:

AbstractQueuedLongSynchronizer.ConditionObject,AbstractQueuedSynchronizer.ConditionObject

public interface Condition

Condition 将Object 监视器方法(waitnotifynotifyAll)分解成截然不同的对象,以便通过将这些对象与任意Lock实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。

条件(也称为条件队列条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。

Condition 实例实质上被绑定到一个锁上。要为特定Lock实例获得 Condition 实例,请使用其newCondition() 方法。

作为一个示例,假定有一个绑定的缓冲区,它支持 put 和 take方法。如果试图在空的缓冲区上执行 take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行 put 操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待 set 中保存 put 线程和 take 线程,这样就可以在缓冲区中的项或空间变得可用时利用最佳规划,一次只通知一个线程。可以使用两个Condition实例来做到这一点。

 classBoundedBuffer {

   final Lock lock = new ReentrantLock();

   final ConditionnotFull  = lock.newCondition();

   final ConditionnotEmpty = lock.newCondition();

 

   final Object[]items = new Object[100];

   int putptr,takeptr, count;

 

   public voidput(Object x) throws InterruptedException {

     lock.lock();

     try {

       while (count== items.length)

         notFull.await();

      items[putptr] = x;

       if (++putptr== items.length) putptr = 0;

       ++count;

       notEmpty.signal();

     } finally {

       lock.unlock();

     }

   }

   public Objecttake() throws InterruptedException {

     lock.lock();

     try {

       while (count== 0)

         notEmpty.await();

       Object x =items[takeptr];

       if(++takeptr == items.length) takeptr = 0;

       --count;

       notFull.signal();

       return x;

     } finally {

      lock.unlock();

     }

   }

 }

ArrayBlockingQueue类提供了这项功能,因此没有理由去实现这个示例类。)

Condition 实现可以提供不同于 Object 监视器方法的行为和语义,比如受保证的通知排序,或者在执行通知时不需要保持一个锁。如果某个实现提供了这样特殊的语义,则该实现必须记录这些语义。

注意,Condition 实例只是一些普通的对象,它们自身可以用作synchronized 语句中的目标,并且可以调用自己的 waitnotification 监视器方法。获取 Condition实例的监视器锁或者使用其监视器方法,与获取和该 Condition 相关的Lock或使用其waitingsignalling 方法没有什么特定的关系。为了避免混淆,建议除了在其自身的实现中之外,切勿以这种方式使用 Condition 实例。

除非另行说明,否则为任何参数传递 null 值将导致抛出 NullPointerException

实现注意事项

在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为Condition 应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。

三种形式的条件等待(可中断、不可中断和超时)在一些平台上的实现以及它们的性能特征可能会有所不同。尤其是它可能很难提供这些特性和维护特定语义,比如排序保证。更进一步地说,中断线程实际挂起的能力在所有平台上并不是总是可行的。

因此,并不要求某个实现为所有三种形式的等待定义完全相同的保证或语义,也不要求其支持中断线程的实际挂起。

要求实现清楚地记录每个等待方法提供的语义和保证,在某个实现不支持中断线程的挂起时,它必须遵从此接口中定义的中断语义。

由于中断通常意味着取消,而又通常很少进行中断检查,因此实现可以先于普通方法的返回来对中断进行响应。即使出现在另一个操作后的中断可能会释放线程锁时也是如此。实现应记录此行为。

从以下版本开始: 1.5

2.2 example

public class ProductFactory2 {

   //创建互斥锁对象

   private Lockl=new ReentrantLock();

   //分别将需要加锁的对象绑定到锁上

   Condition producerL=l.newCondition();

   Condition consumerL=l.newCondition();

   private Stringname;

   //定义阻塞与唤醒的转换器条件

   private booleanhasProduct=false;

   //对外生产手机的方法和获取手机的方法属于互斥共享资源的方法,需要定义为同名锁方法

   //定义对外生产手机的方法

   public voidproduce(){

      l.lock();

      while(hasProduct){

         try {

            producerL.await();

         } catch (InterruptedExceptione) {

            // TODO Auto-generated catch block

            e.printStackTrace();

         }

      }

      this.name="哈哈哈";

      System.out.println(Thread.currentThread().getName()+"生产了一个商品:"+name);

      hasProduct=true;

      consumerL.signal();

      l.unlock();

   }

   //定义对外获取手机的方法

   public voidget(){

      l.lock();

      while(!hasProduct){

         try {

            consumerL.await();

         } catch (InterruptedExceptione) {

            // TODO Auto-generated catch block

            e.printStackTrace();

         }

      }

      System.out.println(Thread.currentThread().getName()+"消费了一个商品:"+name);

      hasProduct=false;

      producerL.signal();

      l.unlock();

   }

}

 

原创粉丝点击