您的位置:首页 > 编程语言 > Java开发

java中的lock

2016-01-27 23:34 375 查看
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: