Android ReentrantLock
阅读原文时间:2023年07月17日阅读:1

synchronized原语和ReentrantLock在一般情况下没有什么区别,但是在非常复杂的同步应用中,请考虑使用ReentrantLock,特别是遇到下面2种需求的时候。 
1.某个线程在等待一个锁的控制权的这段时间需要中断 
2.需要分开处理一些wait-notify,ReentrantLock里面的Condition应用,能够控制notify哪个线程 
3.具有公平锁功能,每个到来的线程都将排队等候 
下面细细道来……

先说第一种情况,ReentrantLock的lock机制有2种,忽略中断锁和响应中断锁,这给我们带来了很大的灵活性。比如:如果A、B2个线程去竞争锁,A线程得到了锁,B线程等待,但是A线程这个时候实在有太多事情要处理,就是一直不返回,B线程可能就会等不及了,想中断自己,不再等待这个锁了,转而处理其他事情。这个时候ReentrantLock就提供了2种机制,第一,B线程中断自己(或者别的线程中断它),但是ReentrantLock不去响应,继续让B线程等待,你再怎么中断,我全当耳边风(synchronized原语就是如此);第二,B线程中断自己(或者别的线程中断它),ReentrantLock处理了这个中断,并且不再等待这个锁的到来,完全放弃。(如果你没有了解java的中断机制,请参考下相关资料,再回头看这篇文章,80%的人根本没有真正理解什么是java的中断,呵呵)

这里来做个试验,首先搞一个Buffer类,它有读操作和写操作,为了不读到脏数据,写和读都需要加锁,我们先用synchronized原语来加锁,如下:

package cn.vicky.chapt10;

/**
*
* @author Vicky.H
*/
public class Buffer {

private Object lock;

public Buffer() {  
    lock = this;  
}

public void write() {  
    synchronized (lock) {  
        long startTime = System.currentTimeMillis();  
        System.out.println("开始往这个buff写入数据…");  
        for (;;)// 模拟要处理很长时间  
        {  
            if (System.currentTimeMillis()  
                    - startTime > Integer.MAX\_VALUE) {  
                break;  
            }  
        }  
        System.out.println("终于写完了");  
    }  
}

public void read() {  
    synchronized (lock) {  
        System.out.println("从这个buff读数据");  
    }  
}

public static void main(String\[\] args) {  
    Buffer buff = new Buffer();

    final Writer writer = new Writer(buff);  
    final Reader reader = new Reader(buff);

    writer.start();  
    reader.start();

    new Thread(new Runnable() {

        @Override  
        public void run() {  
            long start = System.currentTimeMillis();  
            for (;;) {  
                //等5秒钟去中断读  
                if (System.currentTimeMillis()  
                        - start > ) {  
                    System.out.println("不等了,尝试中断");  
                    reader.interrupt();  
                    break;  
                }

            }

        }  
    }).start();  
    // 我们期待“读”这个线程能退出等待锁,可是事与愿违,一旦读这个线程发现自己得不到锁,  
    // 就一直开始等待了,就算它等死,也得不到锁,因为写线程要21亿秒才能完成 T\_T ,即使我们中断它,  
    // 它都不来响应下,看来真的要等死了。这个时候,ReentrantLock给了一种机制让我们来响应中断,  
    // 让“读”能伸能屈,勇敢放弃对这个锁的等待。我们来改写Buffer这个类,就叫BufferInterruptibly吧,可中断缓存。  
}  

}

class Writer extends Thread {

private Buffer buff;

public Writer(Buffer buff) {  
    this.buff = buff;  
}

@Override  
public void run() {  
    buff.write();  
}  

}

class Reader extends Thread {

private Buffer buff;

public Reader(Buffer buff) {  
    this.buff = buff;  
}

@Override  
public void run() {

    buff.read();//这里估计会一直阻塞    

    System.out.println("读结束");

}  

}

package cn.vicky.chapt10;

import java.util.concurrent.locks.ReentrantLock;

/**
*
* @author Vicky.H
*/
public class BufferInterruptibly {

private ReentrantLock lock = new ReentrantLock();

public void write() {  
    lock.lock();  
    try {  
        long startTime = System.currentTimeMillis();  
        System.out.println("开始往这个buff写入数据…");  
        for (;;)// 模拟要处理很长时间  
        {  
            if (System.currentTimeMillis()  
                    - startTime > Integer.MAX\_VALUE) {  
                break;  
            }  
        }  
        System.out.println("终于写完了");  
    } finally {  
        lock.unlock();  
    }  
}

public void read() throws InterruptedException {  
    lock.lockInterruptibly();// 注意这里,可以响应中断  
    try {  
        System.out.println("从这个buff读数据");  
    } finally {  
        lock.unlock();  
    }  
}

public static void main(String args\[\]) {  
    BufferInterruptibly buff = new BufferInterruptibly();

    final Writer2 writer = new Writer2(buff);  
    final Reader2 reader = new Reader2(buff);

    writer.start();  
    reader.start();

    new Thread(new Runnable() {

        @Override  
        public void run() {  
            long start = System.currentTimeMillis();  
            for (;;) {  
                if (System.currentTimeMillis()  
                        - start > ) {  
                    System.out.println("不等了,尝试中断");  
                    reader.interrupt();  
                    break;  
                }  
            }  
        }  
    }).start();

}  

}

class Reader2 extends Thread {

private BufferInterruptibly buff;

public Reader2(BufferInterruptibly buff) {  
    this.buff = buff;  
}

@Override  
public void run() {

    try {  
        buff.read();//可以收到中断的异常,从而有效退出  
    } catch (InterruptedException e) {  
        System.out.println("我不读了");  
    }

    System.out.println("读结束");

}  

}

class Writer2 extends Thread {

private BufferInterruptibly buff;

public Writer2(BufferInterruptibly buff) {  
    this.buff = buff;  
}

@Override  
public void run() {  
    buff.write();  
}

}

2个程序,运行结果:

run:
开始往这个buff写入数据…
不等了,尝试中断

run:
开始往这个buff写入数据…
不等了,尝试中断
我不读了
读结束

‍ReentrantLock是一个互斥的同步器,其实现了接口Lock

一个重要Example:

package tags;

import java.util.Calendar;

public class TestLock {
private ReentrantLock lock = null;

public int data = ;     // 用于线程同步访问的共享数据

public TestLock() {  
    lock = new ReentrantLock(); // 创建一个自由竞争的可重入锁  
}  
public ReentrantLock getLock() {  
    return lock;  
}

public void testReentry() {  
    lock.lock();  
    Calendar now = Calendar.getInstance();  
    System.out.println(now.getTime() + " " + Thread.currentThread()    + " get lock.");  
}

public static void main(String\[\] args) {  
    TestLock tester = new TestLock();

    //1、测试可重入  
    tester.testReentry();  
    tester.testReentry(); // 能执行到这里而不阻塞,表示锁可重入  
    tester.testReentry(); // 再次重入

    // 释放重入测试的锁,要按重入的数量解锁,否则其他线程无法获取该锁。  
    tester.getLock().unlock();  
    tester.getLock().unlock();  
    tester.getLock().unlock();

    //2、测试互斥  
    // 启动3个线程测试在锁保护下的共享数据data的访问  
    new Thread(new workerThread(tester)).start();  
    new Thread(new workerThread(tester)).start();  
    new Thread(new workerThread(tester)).start();  
}

// 线程调用的方法  
public void testRun() throws Exception {  
    lock.lock();

    Calendar now = Calendar.getInstance();  
    try {  
        // 获取锁后显示 当前时间 当前调用线程 共享数据的值(并使共享数据 + 1)  
        System.out.println(now.getTime() + " " + Thread.currentThread()+ " accesses the data " + data++);  
        Thread.sleep();  
    } catch (Exception e) {  
        e.printStackTrace();  
    } finally {  
        lock.unlock();  
    }  
}  

}

// 工作线程,调用TestServer.testRun
class workerThread implements Runnable {

private TestLock tester = null;

public workerThread(TestLock testLock) {  
    this.tester = testLock;  
}

public void run() {  
    try {  
        tester.testRun();  
    } catch (Exception e) {  
        e.printStackTrace();  
    }  
}  

}