详解JUC之锁——ReentrantLock类(03)

前言

在详解JUC之锁——Lock与AQS(02)中我介绍了JUC锁的根基——AQS类还有公平锁和非公平锁,现在就可以正式介绍一下JUC锁家族其中的一个成员——ReentrantLock

ReentrantLock

ReentrantLock是一个互斥锁,也是一个可重入锁(Reentrant就是再次进入的意思)。ReentrantLock锁在同一个时间点只能被一个线程锁持有,但是它可以被单个线程多次获取,每获取一次AQSstate就加1,每释放一次state就减1。还记得synchronized嘛,它也是可重入的,一个同步方法调用另外一个同步方法是没有问题的。

在使用上无非就是获取锁和释放锁,我们完全可以用它来实现synchronized的功能

我要实现一个程序,由两条线程去输出100到0,下面是有问题的程序代码


public class Main {
    public static void main(String[] args) {
        Counter counter = new Counter();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                while(counter.getCount()>=0)
                    counter.desc();
            }
        };

        new Thread(runnable).start();
        new Thread(runnable).start();
    }
}

class Counter{
    private int count = 100;    
    public void desc(){
        System.out.println(Thread.currentThread().getName() +"--->"+count);
        count--;
    }

    public int getCount() {
        return count;
    }
}   

某次执行输出结果,很明显并没有达到我的要求。

.....
Thread-1--->6
Thread-1--->5
Thread-1--->4
Thread-1--->3
Thread-1--->2
Thread-1--->1
Thread-1--->0
Thread-0--->15

于是我用ReentrantLock改写一下Counter类的desc方法,你要注意了,千万不要傻傻地在desc方法内部创建一个ReentrantLock对象,这样每次线程调用的时候用的都是一个新锁,还谈什么互斥呀,就像同步方法和静态同步方法,它们的锁都不是同一个,是互斥不了的。现在运行代码是没错的了

class Counter {
    private int count = 100;
    private Lock lock = new ReentrantLock();

    public void desc() {
        lock.lock();//上锁

        if (count >= 0){
            System.out.println(Thread.currentThread().getName() + "--->" + count);
            count--;
        }

        lock.unlock();//释放锁
    }

    public int getCount() {
        return count;
    }
}

上面代码还是有问题的,那就是锁的释放,如果在上锁了,后面的代码抛出异常没能释放锁,你说完不完蛋!!?所以锁的释放一定要在try-finally块finally中,就像是JDBC中释放数据库连接那样。这一点还是synchronized比较方便,不用我们自己释放锁。

Condition

到了这里就要谈到Condition了,它需要与 Lock 联合使用,它的作用就是代替Object的那些监视器方法,Condition 中的await()signal()signalAll()方法分别对应着Objectwait()notify()notifyAll()方法。

不过一个它比较牛逼的一点是,一个Lock可以关联多个Condition,这样子玩起来就很灵活了,想要各个方法按什么顺序执行都行。还是上面那个例子,我想让两个线程和谐点,你输出一个数,然后我又输出下一个数,这样子交替执行,实现代码如下

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Main {
    public static void main(String[] args) {
        Counter counter = new Counter();

        new Thread(new Runnable() {

            @Override
            public void run() {
                while (counter.getCount() >= 0) {
                    counter.desc1();
                }
            }
        }).start();

        new Thread(new Runnable() {

            @Override
            public void run() {
                while (counter.getCount() >= 0) {
                    counter.desc2();
                }
            }
        }).start();
    }
}

class Counter {
    private int count = 100;
    private Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    boolean state = true;

    public void desc1() {
        lock.lock();// 上锁

        try {
            while (state)
                condition1.await();// 线程等待

            if (count >= 0) {
                System.out.println(Thread.currentThread().getName() + "--->" + count);
                count--;
            }
            state = true;// 改变状态
            condition2.signal();// 唤醒调用了condition2.await()线程

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();// 释放锁
        }
    }

    public void desc2() {
        lock.lock();// 上锁

        try {
            while (!state)
                condition2.await();// 线程等待

            if (count >= 0) {
                System.out.println(Thread.currentThread().getName() + "--->" + count);
                count--;
            }
            state = false;// 改变状态
            condition1.signal();// 唤醒调用了condition1.await()线程

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();// 释放锁
        }
    }

    public int getCount() {
        return count;
    }
}

输出结果

.....
Thread-1--->10
Thread-0--->9
Thread-1--->8
Thread-0--->7
Thread-1--->6
Thread-0--->5
Thread-1--->4
Thread-0--->3
Thread-1--->2
Thread-0--->1
Thread-1--->0

上面代码不难,所以不再这多解释了,不过按照上面的例子你就可以举一反三了,我记得有道线程的题目是让三个线程不断交替输出什么鬼的,t1->t2->t3->t1->t2->t3….,根据上面的例子我相信你能解决这个问题的,可以拿这个题目练一下手,熟悉一下。

最后要提一下的是ReentrantLock有两个构造方法,默认的构造方法会让它成为一个非公平锁,而如果你想创建一个公平锁则用ReentrantLock(boolean fair)传入一个true创建ReentrantLock实例

你可能感兴趣的:(Java基础,juc,线程)