上一篇Java中的队列同步器AQS

1、Lock接口和synchronized内置锁

a)synchronized:Java提供的内置锁机制,Java中的每个对象都可以用作一个实现同步的锁(内置锁或者监视器Monitor),线程在进入同步代码块之前需要或者这把锁,在退出同步代码块会释放锁。而synchronized这种内置锁实际上是互斥的,即没把锁最多只能由一个线程持有。

b)Lock接口:Lock接口提供了与synchronized相似的同步功能,和synchronized(隐式的获取和释放锁,主要体现在线程进入同步代码块之前需要获取锁退出同步代码块需要释放锁)不同的是,Lock在使用的时候是显示的获取和释放锁。虽然Lock接口缺少了synchronized隐式获取释放锁的便捷性,但是对于锁的操作具有更强的可操作性、可控制性以及提供可中断操作和超时获取锁等机制。

2、lock接口使用的一般形式

1 Lock lock = new ReentrantLock(); //这里可以是自己实现Lock接口的实现类,也可以是jdk提供的同步组件
2 lock.lock();//一般不将锁的获取放在try语句块中,因为如果发生异常,在抛出异常的同时,也会导致锁的无故释放
3 try {
4 }finally {
5     lock.unlock(); //放在finally代码块中,保证锁一定会被释放
6 }

3、Lock接口的方法

Java中的锁——Lock和synchronized

 1 public interface Lock {
 2 
 3     /**
 4      * 获取锁,调用该方法的线程会获取锁,当获取到锁之后会从该方法但会
 5      */
 6     void lock();
 7 
 8     /**
 9      * 可响应中断。即在获取锁的过程中可以中断当前线程
10      */
11     void lockInterruptibly() throws InterruptedException;
12 
13     /**
14      * 尝试非阻塞的获取锁,调用该方法之后会立即返回,如果获取到锁就返回true否则返回false
15      */
16     boolean tryLock();
17 
18     /**
19      * 超时的获取锁,下面的三种情况会返回
20      * ①当前线程在超时时间内获取到了锁
21      * ②当前线程在超时时间内被中断
22      * ③超时时间结束,返回false
23      */
24     boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
25 
26     /**
27      * 释放锁
28      */
29     void unlock();
30 
31     /**
32      * 获取等待通知组件,该组件和当前锁绑定,当前线程只有获取到了锁才能调用组件的wait方法,调用该方法之后会释放锁
33      */
34     Condition newCondition();
35 }

4、相比于synchronized,Lock接口所具备的其他特性

①尝试非阻塞的获取锁tryLock():当前线程尝试获取锁,如果该时刻锁没有被其他线程获取到,就能成功获取并持有锁

②能被中断的获取锁lockInterruptibly():获取到锁的线程能够响应中断,当获取到锁的线程被中断的时候,会抛出中断异常同时释放持有的锁

③超时的获取锁tryLock(long time, TimeUnit unit):在指定的截止时间获取锁,如果没有获取到锁返回false

二、重入锁

1、重入锁的概念

  当某个线程请求一个被其他线程所持有的锁的时候,该线程会被阻塞(后面的读写锁先不考虑在内),但是像synchronized这样的内置锁是可重入的,即一个线程试图获取一个已经被该线程所持有的锁,这个请求会成功。重入以为这锁的操作粒度是线程级别而不是调用级别。我们下面说到的ReentrantLock也是可重入的,而除了支持锁的重入之外,该同步组件也支持公平的和非公平的选择。

2、ReentrantLock

a)ReentrantLock实现的可重入性

对于锁的可重入性,需要解决的两个问题就是:

①线程再次获取锁的识别问题(锁需要识别当前要获取锁的线程是否为当前占有锁的线程);

②锁的释放(同一个线程多次获取同一把锁,那么锁的记录也会不同。一般来说,当同一个线程重复n次获取锁之后,只有在之后的释放n次锁之后,其他的线程才能去竞争这把锁)

③ReentrantLock的可重入测试

 1 import java.util.concurrent.locks.Lock;
 2 import java.util.concurrent.locks.ReentrantLock;
 3 
 4 public class TestCR {
 5     Lock lock = new ReentrantLock();
 6     
 7     void m1(){
 8         try{
 9             lock.lock(); // 加锁
10             for(int i = 0; i < 4; i++){
11                 TimeUnit.SECONDS.sleep(1);
12                 System.out.println("m1() method " + i);
13             }
14             m2(); //在释放锁之前,调用m2方法
15         }catch(InterruptedException e){
16             e.printStackTrace();
17         }finally{
18             lock.unlock(); // 解锁
19         }
20     }
21     
22     void m2(){
23         lock.lock();
24         System.out.println("m2() method");
25         lock.unlock();
26     }
27     
28     public static void main(String[] args) {
29         final TestCR t = new TestCR();
30         new Thread(new Runnable() {
31             @Override
32             public void run() {
33                 t.m1();
34             }
35         }).start();
36 
37         new Thread(new Runnable() {
38             @Override
39             public void run() {
40                 t.m2();
41             }
42         }).start();
43     }
44 }
ReentrantLock的可重入测试

相关文章: