java中的lock

lock就像同步块一样是一种线程同步机制,除此之外locks要比java的同步块更加复杂。locks(和其他的更加先进的同步机制)是使用同步块创建的,所以我们完全摆脱synchronize关键字是不可能的。从java5开始,java.util.concurrent.locks包含了几种lock的实现,所以你或许不必实现自己的locks,但是,你依然要知道怎么样使用它们,知道他们背后的实现原理也是非常有帮助的,要想得到更多的细节,可以看一下java.util.concureent.locks.lock接口

一个简单的lock

我们以一个java的同步块代码开始

public class Counter{

  private int count = 0;

  public int inc(){
    synchronized(this){
      return ++count;
    }
  }
}


注意inc()方法中的synchronized(this)块,这个块确保在某一时刻只能有一个线程执行return ++count这个操作。在同步块中的代码可以更好,但是这个++count操作足够了解了。

public class Counter{

  private Lock lock = new Lock();
  private int count = 0;

  public int inc(){
    lock.lock();
    int newCount = ++count;
    lock.unlock();
    return newCount;
  }
}

这个Counter类可以写成如下的方式,使用lock替代同步块lock()方法锁住了Lock实例以至于所有调用lock方法的线程都会阻塞直到unlock方法执行。

下边这是一个简单的lock的实现

public class Lock{

  private boolean isLocked = false;

  public synchronized void lock()
  throws InterruptedException{
    while(isLocked){
      wait();
    }
    isLocked = true;
  }

  public synchronized void unlock(){
    isLocked = false;
    notify();
  }
}


注意这个while(isLocked)循环,也叫作“旋转锁”。旋转锁和方法wait以及notify在线程信号这篇文章中多次讲到了。当isLocked为true的时候,线程调用lock方法在wait方法调用中等待。在这种情况相下没有收到notify调用的时候线程从wait调用中返回的是不确定的,线程要重新检查isLocked条件来看看继续是否是安全的,而不仅仅是假设条件安全的。如果isLocked是假的,线程会退出循环并且设置isLocked为true,为其他线程调用lock方法锁定Lock实例。当线程在关键区完成工作的时候(在lock和unlock之间的代码),线程调用unlock方法,执行unlock方法把isLocked设置回假,叫醒在wait方法中的等待的线程。

Lock Reentrance

java中的同步块是再入的。也就是说,如果一个java线程进入了一个同步代码块,在监控对象上拿到了同步锁并进入同步状态,线程可以其他的java代码块,在相同的监视对象上进行同步。下边这是一个例子

public class Reentrant{

  public synchronized outer(){
    inner();
  }

  public synchronized inner(){
    //do something
  }
}


注意outer方法和inner方法都声明为同步的,在java中和synchronized(this)是等价的。如果线程调用了outer方法毫无疑问它会调用内部的inner方法。因为两个方法或者是块在同一个监视对象(this)上进行的同步,如果一个线程已经拥有了这个监视对象的锁,它可以访问同一个监视对象上的所有的同步代码块,这就叫做重入,线程可以重新进入已经拥有锁的任何的代码块。

早前看到的实现并不是重入,如果我们像下边这样重写Reentrant类,线程调用outer会在inner方法中的lock.lock中阻塞。

public class Reentrant2{

  Lock lock = new Lock();

  public outer(){
    lock.lock();
    inner();
    lock.unlock();
  }

  public synchronized inner(){
    lock.lock();
    //do something
    lock.unlock();
  }
}


一个线程调用outer方法会首先锁上Lock实例,然后调用inner方法。在inner方法中线程再一次的想锁住锁实例,这会失败(意思是线程将会被阻塞),因为锁实例已经在outer方法中被锁住了。线程将会被第二次阻塞的原因是它调用了lock方法而没有调用unlock犯法,我们看看这个lock方法的实现之后就很明显了

public class Lock{

  boolean isLocked = false;

  public synchronized void lock()
  throws InterruptedException{
    while(isLocked){
      wait();
    }
    isLocked = true;
  }

  ...
}


注意while循环现在把锁实例也考虑进去了,或者锁被解锁调用线程是锁实例。在while循环中的条件决定是否线程要退出,当前情况下isLocked必须是false才可以,而不管线程是否已经锁住它了。为了让锁类重入,我们需要做一些小小的改变

public class Lock{

  boolean isLocked = false;
  Thread  lockedBy = null;
  int     lockedCount = 0;

  public synchronized void lock()
  throws InterruptedException{
    Thread callingThread = Thread.currentThread();
    while(isLocked && lockedBy != callingThread){
      wait();
    }
    isLocked = true;
    lockedCount++;
    lockedBy = callingThread;
  }


  public synchronized void unlock(){
    if(Thread.curentThread() == this.lockedBy){
      lockedCount--;

      if(lockedCount == 0){
        isLocked = false;
        notify();
      }
    }
  }

  ...
}


注意while循环现在把锁实例也考虑进去了,或者锁被解锁调用线程是锁实例

翻译的不好,还有一些没有翻译出来,想看原文的同学可以看下边的链接

原文地址:http://tutorials.jenkov.com/java-concurrency/locks.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值