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

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

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個(gè)thread同時(shí)執(zhí)行到這句時(shí),只有第一個(gè)thread能獲得這個(gè)lock,其他thread都必須擋在那,在后邊排隊(duì)。catch 不能放在本級方法調(diào)用中,否則當(dāng)lock被打斷后,繼續(xù)優(yōu)雅執(zhí)行,lock.unlock();被執(zhí)行時(shí),就會(huì)出現(xiàn)lock狀態(tài)不對的問題(Exception in thread  java.lang.IllegalMonitorStateException,報(bào)完這個(gè)exception后,會(huì)在lock.unlock()這句話直接崩潰不能優(yōu)雅結(jié)束),因?yàn)閘ock已經(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