Java并发编程学习(8)---ReentrantLock学习

目录页:https://blog.csdn.net/u011294519/article/details/88367808

1. ReentrantLock

1.1.小声哔哔

    年轻的时候,说到锁想到的就是synchronized关键字,而且那时候项目要求不高,已经完全能满足需求,但是深入了解synchronized以后发现自己真的是年轻(貌似说synchronized已经被优化的与Lock差不多,但是真的了解过后发现由于synchronized本身的限制导致在读写锁方面是synchronized永远也赶不上的)。

    ReentrantLock从命名上来看就是可重入,但是synchronized其实也是可重入的

ReentrantLock与synchronized的区别:

  1. synchronized是依赖于JVM实现的,而ReenTrantLock是JDK实现的。
  2. 如果一个方法被Synchronized修饰作为对象锁,甚至是类锁(本质也是对象锁),那么一个线程获取到锁后其他线程就被阻塞,直到该线程释放锁。ReentrantLock支持设置等待时间,并且可阻断。
  3. 在释放锁的方式上,synchronized在开发过程中完全可以不用去关心,在方法执行完成或执行异常后,JVM会将锁释放,这也是内置关键字的好处。而ReentrantLock则需要我们显示的释放锁,而且必须是要写在finally中防止异常导致死锁。由此可以看出在使用方式上ReenTrantLock比synchronized更加灵活,但是synchronized使用起来更加简单。

1.2. ReentrantLock的一些特性

  1. ReenTrantLock可以通过构造方法指定使用公平锁还是非公平锁。Synchronized关键字只能是非公平锁。(PS:公平锁就是FIFO这种,但是非公平锁效率会更高,因为公平锁要加判断等进行实现,有这功夫活都干完了)
  2. ReenTrantLock利用AQS提供了一个Condition(条件)类,可以对线程分组唤醒,而synchronized则是会唤醒全部线程来竞争锁。
  3. ReenTrantLock可以使用tryLock尝试获取锁,并不是一直阻塞。

1.3.ReentrantLock主要方法

    构造方法ReentrantLock():初始化非公平锁

    构造方法public ReentrantLock(boolean fair):传入true初始化公平锁,若传入参数false则初始化非公平锁。

    lock():获取锁

    unlock():释放锁

    boolean tryLock():尝试获取锁,若获取成功则返回true

    boolean tryLock (long timeout, TimeUnit unit):尝试获取锁,若此时无法获取锁在等待timeout时间后放弃获取锁并返回false。

    lockInterruptibly():以响应中断的方式加锁

1.4. 上代码

1.4.1. 实现与synchronized相同效果

    先使用ReentrantLock达到和synchronized相同的效果,使用递归体现可重入

package com.concurrent.aqslock.part8.reent;

import java.util.concurrent.locks.ReentrantLock;

/**
 * 使用显示锁的范式
 */
public class LockDemo {
    private static ReentrantLock lock = new ReentrantLock();

    /**
     * 使用ReentrantLock锁
     */
    static class ReenTrantThread implements Runnable {
        private int count = 0;

        @Override
        public void run() {
            cyclic();
        }

        private void cyclic() {
            lock.lock();
            try {
                while (count < 2) {
                    System.out.println(Thread.currentThread().getName() + " count value: " + count);
                    Thread.sleep(1000);
                    ++count;
                    System.out.printf("Lock is get by CurrentThread: %s %s \n", Thread.currentThread().getName(), lock.isHeldByCurrentThread());
                    cyclic();
                    Thread.sleep(3000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (count >= 2 && lock.isHeldByCurrentThread()) {
                    System.out.printf("%s unlock ,queue length: %s \n", Thread.currentThread().getName(), lock.getQueueLength());
                    lock.unlock();
                }
            }
        }
    }

    /**
     * 使用synchronized锁
     */
    static class SysThread implements Runnable {
        private int count = 0;

        @Override
        public void run() {
            cyclic();
        }

        private synchronized void cyclic() {
            while (count < 2) {
                System.out.println(Thread.currentThread().getName() + " count value: " + count);
                ++count;
                cyclic();
            }
        }
    }

    public static void main(String[] arg0) throws InterruptedException {
        for (int i = 0; i < 2; ++i) {
            new Thread(new ReenTrantThread(), "ReenTrantThread " + i).start();
        }

        for (int i = 0; i < 2; ++i) {
            new Thread(new SysThread(), "SysThread " + i).start();
        }

        Thread.sleep(3000);
    }

}

运行结果:

代码位置:aqs-lock的part8

1.4.2. 使用可中断式获取锁

package com.concurrent.aqslock.part8.reent;

import java.util.concurrent.locks.ReentrantLock;

/**
 * 演示可中断式获取锁
 */
public class InterruptReenTrant {
    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] arg0) {

        Thread t1 = new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName() + " before get lock");
                lock.lockInterruptibly();
                Thread.sleep(3000);
                System.out.println(Thread.currentThread().getName() + " after get lock");

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    System.out.println(Thread.currentThread().getName() + " unlock");
                    lock.unlock();
                }
            }
        }, "first thread");

        Thread t2 = new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName() + " before get lock");
                lock.lockInterruptibly();
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + " after get lock");

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    System.out.println(Thread.currentThread().getName() + " unlock");
                    lock.unlock();
                }
            }
        }, "second thread");

        t1.start();
        t2.start();
        // 尝试注释和不注释下面代码
        t1.interrupt();
    }

}

运行结果:

    代码位置:aqs-lock的part8

1.4.3. tryLock

package com.concurrent.aqslock.part8.reent;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 演示tryLock
 */
public class TryLockReenTrant {
    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] arg0) {
        Thread t1 = new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName() + " before try lock");
                if (lock.tryLock()) {
                    Thread.sleep(3000);
                    System.out.println(Thread.currentThread().getName() + " after try lock");
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    System.out.println(Thread.currentThread().getName() + " unlock");
                    lock.unlock();
                }
            }
        }, "first thread");

        Thread t2 = new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName() + " before try lock");
                if (lock.tryLock()) {
                    Thread.sleep(2000);
                    System.out.println(Thread.currentThread().getName() + " after try lock");
                } else {
                    System.out.println(Thread.currentThread().getName() + " try lock fail");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    System.out.println(Thread.currentThread().getName() + " unlock");
                    lock.unlock();
                }
            }
        }, "second thread");

        Thread t3 = new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName() + " before try lock");
                if (lock.tryLock(6L, TimeUnit.SECONDS)) {
                    Thread.sleep(2000);
                    System.out.println(Thread.currentThread().getName() + " after try lock");
                } else {
                    System.out.println(Thread.currentThread().getName() + " try lock fail");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    System.out.println(Thread.currentThread().getName() + " unlock");
                    lock.unlock();
                }
            }
        }, "third thread");

        t1.start();
        t2.start();
        t3.start();
    }
}

运行结果:

    代码位置:aqs-lock的part8

1.5. 看代码

    非公平队列的Lock():

    可以看到,使用了我们的老朋友AQS的compareAndSetState方法,若state为0时则置为1,设置锁的拥有者为当前线程。

    若更新state失败,则进入acquire方法

    若获取到锁的为当前现在,则给state加1,并直接返回

    若获取到锁的不是当前线程,则进入acquireQueued方法

    首先将当前线程加入等待队列,并挂起当前线程。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值