6、Lock

java.util.concurrent.locks

Interface Lock

所有实现类:

ReentrantLock、ReentrantReadWriteLock.ReadLock、ReentrantReadWriteLock.writeLock

Lock的实现比使用synchronized获得更多的扩展操作。它允许更加灵活的结构,也许是含有不同的属性,或者是支持更多与Condition相关的对象。

 

lock是一种工具,用来控制共享资源在多线程中的访问。一般的,lock提供排他性,也就是同一时间只允许一个线程拿到锁访问共享资源。但是,有些锁能够允许并发

访问共享资源,比如读锁,ReadWriteLock。

使用synchronized方法或者代码块要获得与每个对象进行关联的隐式监听器锁,使用synchronized方法或者代码块使得编程比较简单,避免了在使用lock当中的一些错误。

如果我们想更加灵活的使用锁,才考虑使用lock。

A—B—C—D—E

如这种应用:获得A、B的锁,然后释放A,获得C;释放B获得D;释放C获得E

 

lock锁代码架构如下:

 1 class X {
 2   private final Lock lock=new ReentrantLock();
 3  
 4   public void m() {
 5      lock.lock();//阻塞直到获得条件
 6      try {
 7         //方法体
 8      }finally {
 9         lock.unlock();//防止在方法体中抛出异常导致锁不释放
10      }
11 
12   }
13 }

 

Reentrant 单词释义:可重入的

当一个线程请求其他线程已经占有的锁时,请求线程将被阻塞。可重入就是线程试图获得它自己占有的锁时,请求会成功。重入意味着所请求是基于“每线程(per-thread)”,而不是基于"每调用(per-invocation)"。

重入的实现是通过给每个锁关联一个请求计数(acquisition count)和一个占有它的线程。当计数为0时,认为锁是未被占有的。线程请求一个未被占有的锁时,JVM将记录锁的占有者,并且将请求计数设置为1.

如果同一个线程再次请求这个锁,计数将递增;每次占用线程退出同步块,计数器值将递减。直到计数器达到0,锁被释放。

重进入方便了锁行为的封装,因此简化了面向对象并发代码的开发。子类覆写了父类synchronized类型的方法,并调用父类中的方法。如果没有重进入,这段看上去很自然的代码会产生死锁。因为Widget和

LoggingWidget中的doSomething方法都是synchronized类型,都会在处理前试图获得Widget的锁。倘若锁不是可重入的,super.doSomething的调用者永远无法得到Widget的锁,因为锁已经被占用,

导致线程会永久的延迟,等待着一个永远无法获得的锁。重进入帮助我们避免了这种死锁。

 1 public class Widget {
 2   public synchronized void doSomething() {
 3        .....
 4   }
 5 }
 6 
 7 public class LoggingWidget extends Widget {
 8     public synchronized void doSomething() {
 9           System.out.println("calling doSomething");
10           super.doSomething();
11     }
12 }

额外信息:

1、内部锁是可重入的锁,当然ReentrantLock可重入的锁必然也是可重入的

2、synchronized关键字的方法是可以被覆盖的

3、synchronized关键字锁的对象时内部隐性对象this属性。

 Widget w=new LoggingWidget();

 w.doSomething();

此时,方法到底是运行子类还是父类当中的方法呢?在这里我们看等式的右边new的是哪个类在运行时就会调用哪个类的方法。

而此时它是需要获得子类的锁还是父类的锁对象呢?这个是看等式左边的类型。

这也告诉我们,方法运行看new的类,获得的属性看变量的类型。

posted on 2015-05-27 13:54  飞机说之代码也疯狂  阅读(192)  评论(0编辑  收藏  举报