java中ReentrantLock徹底解決并發(fā)線程的無限等待

ReentrantLock徹底解決并發(fā)線程的無限等待
馬克-to-win:上面的例子,只能做到根據(jù)請求Synchronized方法的隊列里的線程的數(shù)量,決定我是否進入隊列等待。但是一旦決定了等待,進入了等待隊列以后,就無法退出隊列。想達到這個效果,必須要用到ReentrantLock的技術(shù)。ReentrantLock翻譯成中文就是可重入鎖。下面這段話比較難,新手可忽略。和可重入鎖相對的就是不可重入鎖,又名自旋鎖。為什么叫不可重入鎖?因為一旦進入一個帶鎖的方法,你在這個方法當(dāng)中,如果想再進入另外一個帶鎖的方法,就進不去了,好像自己給自己上了鎖(自旋)因為你在第一個方法當(dāng)中你還沒有解開鎖。而可重入鎖在判斷中加了一條是不是本個線程?如是,就隨便進入當(dāng)前對象所有帶鎖的方法。如果對我以上這段話,老手也是不理解的話,可參考我參考目錄中的一個參考網(wǎng)頁。注意sun公司的 ReentrantLock是個類,而sun公司的Lock是個接口。所以為求簡單,我們的例子中就用ReentrantLock, ReentrantLock就是為了解決 Synchronized技術(shù)的很多弊病而生的。缺點就是使用復(fù)雜,簡單問題還用 Synchronized就挺好。馬克-to-win:因為ReentrantLock類中的lockInterruptibly();方法能夠讓正在想獲得鎖的線程被其他線程中斷(見下例),從而打消原來要獲得鎖的計劃。當(dāng)然如果沒有其他的線程占有鎖的話,lockInterruptibly();方法也可以讓當(dāng)前線程從容獲得鎖。
馬克- to-win:馬克 java社區(qū):防盜版實名手機尾號: 73203。
另外底下的例子有點需要注意,lock.lockInterruptibly();的方法的catch部分要放在上一級的方法調(diào)用中。馬克-to-win:換句話說,就故意讓它在被打斷時在本級方法中崩潰,回到上一級。否則的話,如果本級方法能夠優(yōu)雅的執(zhí)行完,執(zhí)行到 lock.unlock();就會出現(xiàn)問題。(Exception in thread java.lang.IllegalMonitorStateException,報完這個exception后,會在lock.unlock()這句話直接崩潰不能優(yōu)雅結(jié)束)因為鎖已經(jīng)被打斷怎么還能unlock呢?另外注意正常的鎖的lock.unlock別忘了必須執(zhí)行。否則程序的鎖的狀態(tài) (lock hold count)就錯了。lockInterruptibly():Acquires the lock if it is not held by another thread and returns immediately, setting the lock hold count to one. If the current thread already holds this lock then the hold count is incremented by one and the method returns immediately. 




例1.9.7:(本例模仿七個人想排隊,但后來兩個人主動放棄排隊,自己打斷自己。)

import java.util.concurrent.locks.ReentrantLock;
class A {
    private ReentrantLock lock = new ReentrantLock();
    int ticketNum = 10;
    public void buyOne() throws InterruptedException {
        System.out.println("just before lock.lockInterruptibly();");
/*lock.lockInterruptibly();的意思就是,當(dāng)前線程獲取了一把可打斷的鎖。當(dāng)有n個thread同時執(zhí)行到這句時,只有第一個thread能獲得這個lock,其他thread都必須擋在那,在后邊排隊。catch 不能放在本級方法調(diào)用中,否則當(dāng)lock被打斷后,繼續(xù)優(yōu)雅執(zhí)行,lock.unlock();被執(zhí)行時,就會出現(xiàn)lock狀態(tài)不對的問題(Exception in thread  java.lang.IllegalMonitorStateException,報完這個exception后,會在lock.unlock()這句話直接崩潰不能優(yōu)雅結(jié)束),因為lock已經(jīng)被打斷了。換句話說,lock一旦被打斷,必須確保lock.unlock()不能被執(zhí)行。*/
        lock.lockInterruptibly();
        System.out.println(Thread.currentThread().getName() + "ticketNum is"
                + ticketNum);
        if (ticketNum > 0) {
            ticketNum--;
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("模仿select * from table for update,執(zhí)行的很慢,買了一張"
                    + Thread.currentThread().getName() + "ticketNum is"
                    + ticketNum);
        }
        lock.unlock();//釋放鎖
    }

    public void buyBatch(int num) throws InterruptedException {
        System.out.println("just before lock.lockInterruptibly();");
        lock.lockInterruptibly();
        System.out.println("Thread.currentThread().getName()+ticketNum is"
                + ticketNum);
        if (ticketNum >= num) {
            ticketNum = ticketNum - num;
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("模仿select * from table for update,執(zhí)行的很慢,買了幾張"
                    + Thread.currentThread().getName() + "ticketNum is"
                    + ticketNum);
        }
        lock.unlock();
    }
}

class MyThread1 extends Thread {
    A a;
    public MyThread1(A a) {
        this.a = a;
    }
    public void run() {
        try {
            a.buyOne();
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName() + "取消一張");
            System.out.println(e);
        }
    }
}

class MyThread2 extends Thread {
    A a;
    public MyThread2(A a) {
        this.a = a;
    }
    public void run() {
        try {
            a.buyBatch(3);
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName() + "取消幾張");
            System.out.println(e);
        }
    }
}

public class TestMark_to_win {
    public static void main(String[] args) throws InterruptedException {
        MyThread1[] threads = new MyThread1[6];
        A a = new A();
        for (int i = 0; i < 6; i++) {
            threads[i] = new MyThread1(a);
        }

MyThread2 myThread2 = new MyThread2(a);

        threads[0].start();
        threads[1].start();
        threads[2].start();
        threads[3].start();
        threads[4].start();
        myThread2.start();
        threads[5].start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        threads[4].interrupt();
        threads[2].interrupt();
    }
}



輸出結(jié)果是:
just before lock.lockInterruptibly();
Thread-0ticketNum is10
just before lock.lockInterruptibly();
just before lock.lockInterruptibly();
just before lock.lockInterruptibly();
just before lock.lockInterruptibly();
just before lock.lockInterruptibly();
just before lock.lockInterruptibly();
Thread-4取消一張
java.lang.InterruptedException
Thread-2取消一張
java.lang.InterruptedException
模仿select * from table for update,執(zhí)行的很慢,買了一張Thread-0ticketNum is9
Thread-1ticketNum is9
模仿select * from table for update,執(zhí)行的很慢,買了一張Thread-1ticketNum is8
Thread-3ticketNum is8
模仿select * from table for update,執(zhí)行的很慢,買了一張Thread-3ticketNum is7
Thread.currentThread().getName()+ticketNum is7
模仿select * from table for update,執(zhí)行的很慢,買了幾張Thread-6ticketNum is4
Thread-5ticketNum is4
模仿select * from table for update,執(zhí)行的很慢,買了一張Thread-5ticketNum is3