Android ReentrantLock


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 > 5000) {
                        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 > 5000) {
                        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 = 100;     // 用於線程同步訪問的共享數據

    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(1000);
        } 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();
        }
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM