java中ReentrantLock彻底解决并发线程的无限等待

ReentrantLock彻底解决并发线程的无限等待
马克-to-win:上面的例子,只能做到根据请求Synchronized方法的队列里的线程的数量,决定我是否进入队列等待。但是一旦决定了等待,进入了等待队列以后,就无法退出队列。想达到这个效果,必须要用到ReentrantLock的技术。ReentrantLock翻译成中文就是可重入锁。下面这段话比较难,新手可忽略。和可重入锁相对的就是不可重入锁,又名自旋锁。为什么叫不可重入锁?因为一旦进入一个带锁的方法,你在这个方法当中,如果想再进入另外一个带锁的方法,就进不去了,好像自己给自己上了锁(自旋)因为你在第一个方法当中你还没有解开锁。而可重入锁在判断中加了一条是不是本个线程?如是,就随便进入当前对象所有带锁的方法。如果对我以上这段话,老手也是不理解的话,可参考我参考目录中的一个参考网页。注意sun公司的 ReentrantLock是个类,而sun公司的Lock是个接口。所以为求简单,我们的例子中就用ReentrantLock, ReentrantLock就是为了解决 Synchronized技术的很多弊病而生的。缺点就是使用复杂,简单问题还用 Synchronized就挺好。马克-to-win:因为ReentrantLock类中的lockInterruptibly();方法能够让正在想获得锁的线程被其他线程中断(见下例),从而打消原来要获得锁的计划。当然如果没有其他的线程占有锁的话,lockInterruptibly();方法也可以让当前线程从容获得锁。
马克- to-win:马克 java社区:防盗版实名手机尾号: 73203。
另外底下的例子有点需要注意,lock.lockInterruptibly();的方法的catch部分要放在上一级的方法调用中。马克-to-win:换句话说,就故意让它在被打断时在本级方法中崩溃,回到上一级。否则的话,如果本级方法能够优雅的执行完,执行到 lock.unlock();就会出现问题。(Exception in thread java.lang.IllegalMonitorStateException,报完这个exception后,会在lock.unlock()这句话直接崩溃不能优雅结束)因为锁已经被打断怎么还能unlock呢?另外注意正常的锁的lock.unlock别忘了必须执行。否则程序的锁的状态 (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();的意思就是,当前线程获取了一把可打断的锁。当有n个thread同时执行到这句时,只有第一个thread能获得这个lock,其他thread都必须挡在那,在后边排队。catch 不能放在本级方法调用中,否则当lock被打断后,继续优雅执行,lock.unlock();被执行时,就会出现lock状态不对的问题(Exception in thread  java.lang.IllegalMonitorStateException,报完这个exception后,会在lock.unlock()这句话直接崩溃不能优雅结束),因为lock已经被打断了。换句话说,lock一旦被打断,必须确保lock.unlock()不能被执行。*/
        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,执行的很慢,买了一张"
                    + 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,执行的很慢,买了几张"
                    + 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();
    }
}



输出结果是:
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,执行的很慢,买了一张Thread-0ticketNum is9
Thread-1ticketNum is9
模仿select * from table for update,执行的很慢,买了一张Thread-1ticketNum is8
Thread-3ticketNum is8
模仿select * from table for update,执行的很慢,买了一张Thread-3ticketNum is7
Thread.currentThread().getName()+ticketNum is7
模仿select * from table for update,执行的很慢,买了几张Thread-6ticketNum is4
Thread-5ticketNum is4
模仿select * from table for update,执行的很慢,买了一张Thread-5ticketNum is3