Java中的ReentranLock

在Java多线程中,可以使用synchronized关键字来实现线程之间的同步互斥,但在JDK1.5中新增加的ReentranLock也能达到同样的效果,并且在扩展功能上也更加强大。

一、使用lock方法和unlock方法实现同步

    ReentranLock的lock方法用来获取锁,而调用unlock的方法是释放锁。

public class Test1 {
    public static void main(String[] args) throws InterruptedException {
        Service service = new Service();
        ThreadA a = new ThreadA(service);
        a.setName("A");
        ThreadA aa = new ThreadA(service);
        aa.setName("AA");
        Thread.sleep(100);
        ThreadB b = new ThreadB(service);
        b.setName("B");
        ThreadB bb = new ThreadB(service);
        bb.setName("BB");
        bb.start();
        a.start();
        aa.start();
        b.start();
    }
}
class ThreadA extends Thread{
    private Service service;
    public ThreadA(Service service){
        this.service = service;
    }
    @Override
    public void run() {
        service.methodA();
    }
}
class ThreadB extends Thread{
    private Service service;
    public ThreadB(Service service){
        this.service = service;
    }
    @Override
    public void run() {
        service.methodB();
    }
}
class Service{
    private ReentrantLock lock = new ReentrantLock();
    public void methodA(){
        try {
            lock.lock();
            System.out.println("A  ThreadName = "+Thread.currentThread().getName());
            Thread.sleep(2000);
            System.out.println("A  ThreadName = "+Thread.currentThread().getName());
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void methodB(){
        try {
            lock.lock();
            System.out.println("B  ThreadName = "+Thread.currentThread().getName());
            Thread.sleep(2000);
            System.out.println("B  ThreadName = "+Thread.currentThread().getName());
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

上述代码使用lock方法和unlock方法实现了线程间的同步。输出结果如下


结果说明,调用lock方法的代码的线程就持有了对象监视器,其他线程只有等待锁被释放时再次争抢,效果和使用sunchronized关键字一样。

二、使用Condition实现等待/通知。

使用synchronized关键字以及wait方法和notify方法相结合可以实现等待/通知模式,而ReentranLock也可以实现同样的功能,但需要借助于Condition对象。

你可以在一个ReentranLock对象里面创建多个Condition实例(对象监视器),线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,在调度线程上更加灵活。

public class Test2 {
    public static void main(String[] args) throws InterruptedException {
        Service1 service1 = new Service1();
        ThreadAA threadAA = new ThreadAA(service1);
        threadAA.setName("A");
        threadAA.start();
        Thread.sleep(1000);
        service1.singal();
    }
}
class Service1{
    private ReentrantLock lock = new ReentrantLock();
    public Condition condition = lock.newCondition();
    public void await(){
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName()+"我在等待通知的到来1");
            condition.await();
            System.out.println(Thread.currentThread().getName()+"等到通知了,我继续执行");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void singal(){
        try {
            lock.lock();
            System.out.println("我要通知在等待的线程");
            condition.signal();
        }finally {
            lock.unlock();
        }
    }
}
class ThreadAA extends Thread{
    private Service1 service1;
    public ThreadAA(Service1 service1){
        this.service1 = service1;
    }
    @Override
    public void run() {
        service1.await();
    }
}

上述代码就描述了Condition的用法,Condition在调用await方法之前必须调用lock方法来获得同步监视器。否则会报错。

而singal方法可以唤醒同一个Condition(对象监视器)监视下的线程。

输出结果如下:


等待一秒后通知


线程继续执行未完的代码。其中wait方法相当于await方法,notify方法相当于singal方法,notifyAll方法相当于singalAll方法。

刚才我们只是使用一个Condition方法通知一个线程,这次我们使用多个Condition通知多个线程。

public class Test3 {
    public static void main(String[] args) throws InterruptedException {
        Service2 service2 = new Service2();
        Thread1 thread1 = new Thread1(service2);
        thread1.start();
        Thread2 thread2 = new Thread2(service2);
        thread2.start();
        Thread.sleep(2000);
        service2.singalA();
        Thread.sleep(2000);
        service2.singalB();
    }
}
class Thread1 extends Thread{
    private Service2 service2 = new Service2();
    public Thread1(Service2 service2){
        this.service2 = service2;
    }
    @Override
    public void run() {
        service2.awaitA();
    }
}
class Thread2 extends Thread{
    private Service2 service2 = new Service2();
    public Thread2(Service2 service2){
        this.service2 = service2;
    }
    @Override
    public void run() {
        service2.awaitB();
    }
}
class Service2{
    private ReentrantLock lock = new ReentrantLock();
    public Condition condition1 = lock.newCondition();
    public Condition condition2 = lock.newCondition();
    public void awaitA(){
        try {
            lock.lock();
            System.out.println("awaitA begin 在等待通知 ");
            condition1.await();
            System.out.println("awaitA end 等到通知");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void awaitB(){
        try {
            lock.lock();
            System.out.println("awaitB begin 在等待通知 ");
            condition2.await();
            System.out.println("awaitB end 等到通知");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void singalA(){
        try {
            lock.lock();
            System.out.println("我要通知awaitA");
            condition1.signal();
        }finally {
            lock.unlock();
        }
    }
    public void singalB(){
        try {
            lock.lock();
            System.out.println("我要通知awaitB");
            condition2.signal();
        }finally {
            lock.unlock();
        }
    }
}
上述代码有两个线程,线程1执行awaitA方法,线程2执行awaitB方法,而singalA只唤醒conditon1监视的线程,而singalB只唤醒condition2监视的线程。运行结果如下


开始,线程1和线程2都在等待通知


两秒之后,调用singalA唤醒线程1,线程1收到通知并执行未完成的代码,而线程2仍然在等待


再过两秒,调用singalB方法,通知线程2,线程2收到通知,并完成执行

三、公平锁与非公平锁

    ReentranLock分为公平锁和非公平锁,公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的顺序。而非公平锁就是一种锁的抢占机制,是随机获得锁的,可能会导致某些线程一致拿不到锁,所以是不公平的。

public class Test4 {
    public static void main(String[] args) {
        Service3 service3 = new Service3(true);
        Thread[] threads = new Thread[10];
        for (int i=0;i<10;i++){
            threads[i] = new Thread(){
                @Override
                public void run() {
                    service3.testMethod();
                }
            };
            threads[i].setName("线程"+(i+1));
        }

        for (int i=0;i<10;i++){
            threads[i].start();
        }
    }
}
class Service3{
    private ReentrantLock lock;
    public Service3(boolean isFair){
        lock = new ReentrantLock(isFair);
    }
    public void testMethod(){
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName()+"获得锁");
        }finally {
            lock.unlock();
        }
    }
}

上述代码是使用公平锁,在构造ReentranLock时,有一个布尔值,为true代表公平锁,默认为非公平锁。


从结果来看,基本呈有序状态,这就是公平锁


而这个是非公平锁,可以看出基本是乱序。

总结:

    1、ReentranLock使用lock和unlock来获得锁和释放锁

    2、unlock最好的地方就是finally中,因为这样正常运行或者异常运行都会释放锁

    3、使用Condition可以进行线程间通信,而且使用非常灵活,记住,使用condition的await和singal方法之前,必须调用lock方法获得对象监视器

    4、公平锁和非公平锁基本没什么说的啦

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值