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;
    }
}   


   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

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

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

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

于是我用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;
    }
}
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

上面代码还是有问题的,那就是锁的释放,如果在上锁了,后面的代码抛出异常没能释放锁,你说完不完蛋!!?所以锁的释放一定要在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;
    }
}

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84

输出结果

.....
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

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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

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

转载自:

https://blog.csdn.net/TimHeath/article/details/71643008

你可能感兴趣的:(Java并发编程)