ReentrantLock介绍

ReentrantLock

ReentantLock是java中重入锁的实现,一次只能有一个线程来持有锁,包含三个内部类,SyncNonFairSyncFairSync

1、构造函数

无参构造,默认使用的是非公平性锁

public ReentrantLock() {
        sync = new NonfairSync();
    }

有参构造, Boolean类型的参数 true:表示公平性锁 false:非公平性锁

    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

reentantlock是lock接口的实现类,即实现了Lock接口下所有的方法;
获取锁的方法lock、trylock、lockintertuptibly加锁方式以及释放锁方法

2、公平性锁和非公平性锁

(1)公平性锁和非公平性锁示例

NonFairAndFairDemo类

import java.util.concurrent.locks.ReentrantLock;

public class NonFairAndFairDemo implements Runnable {
    //静态变量(线程共享)
    private static int num = 0;
    //锁实例
    private ReentrantLock rtl;

    public NonFairAndFairDemo(ReentrantLock rtl) {
        this.rtl = rtl;
    }

    @Override
    public void run() {
        while (true) {
            //加锁
            rtl.lock();
            num++;
            System.out.println(Thread.currentThread().getName() + ":" + num);
            rtl.unlock();
        }
    }
}

测试公平锁

@Test
public void test01() {
       ReentrantLock reentrantLock = new ReentrantLock(true);

       Thread threadA = new Thread(new NonFairAndFairDemo(reentrantLock));
       threadA.setName("A");
       Thread threadB = new Thread(new NonFairAndFairDemo(reentrantLock));
       threadB.setName("B");

       threadA.start();
       threadB.start();
}

执行结果
在这里插入图片描述
公平向锁特征如上:按照线程的访问顺序进行获取。


测试非公平锁

@Test
public void test02() {
    ReentrantLock reentrantLock = new ReentrantLock(false);

    Thread threadA = new Thread(new NonFairAndFairDemo(reentrantLock));
    threadA.setName("A");
    Thread threadB = new Thread(new NonFairAndFairDemo(reentrantLock));
    threadB.setName("B");

    threadA.start();
    threadB.start();
}

执行结果
在这里插入图片描述
非公平性锁的特点,是每个线程都连续执行多次之后在替换成其他线程执行。

(2)公平锁和非公平锁的实现
abstract static class Sync extends AbstractQueuedSynchronizer

公平性锁非公平性锁的父类是syncsync类是AbstractQueuedSynchronizer是其子类,AQS是一个同步器,提供同步功能。

abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        //加锁操作,声明是抽象方法,nofairsync和fairsync中各自实现
        abstract void lock();

        //非公平获取,公平性锁和非公平性锁都需要这个方法
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //AQS获取state值 
            int c = getState();
            if (c == 0) {
                //锁空闲状态
                //通过cas获取锁状态,修改state状态
                if (compareAndSetState(0, acquires)) {
                    //标记当前线程为获取锁的线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                //锁非空闲,表明锁被占中,有一种情况,当前线程即为占用锁的线程
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                //当前线程继续持有锁,仅对state进行加操作
                setState(nextc);
                return true;
            }
            return false;
        }

        //释放锁  sync中的tryRelease是公平性锁和非公平性锁的释放锁流程都是该方法
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            //只有持有锁的线程才能释放锁
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {//锁才会被释放
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

       //判断当前线程是否持有锁
        protected final boolean isHeldExclusively() {
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        //获取锁的持有者线程
        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

        //加锁的次数
        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }

        //是否上锁  true:表示加锁
        final boolean isLocked() {
            return getState() != 0;
        }
}

该Sync中方法的封装是调用AQS中的方法实现的

公平性锁和非公平锁的如何实现?

公平性锁:FairLock
static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                //同步状态为空闲,需要等待队列中第一个等待着执行
                //什么时候当前线程可以执行? 等待队列里没有线程等待或者是有线程等待且等待的第一个线程就是当前线程
                if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
           
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                //当前同步状态非空闲,被线程占用且是当前线程
                int nextc = c + acquires;
                if (nextc < 0)
                    //有符号的int类型。最高位为1表示负数
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }AbstractQueuedSynchronizer#acquire
    public final void acquire(int arg) {
    //当前同步状态非空闲,并且是其他线程持有锁 返回false
        if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

公平性锁获取锁流程:
1、如果同步状态为空,就可以抢锁,能够获取锁的前提条件是当前等待队列为空,或者等待队列队头是当前线程,即当前线程才能够抢锁,通过CAS抢锁(state)抢锁成功记录当前线程信息到锁上。
2、如果同步状态不为空,即存在线程占用锁且占用线程是当前线程,当前线程可成功获取锁(state)。

非公平性锁
static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        final void lock() {
            //执行Lock操作,尝试立即获取锁,失败就退回常规流程
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);//立即获取锁失败进入到acquire,首先调用tryAcquire
        }

        protected final boolean tryAcquire(int acquires) {
            //同步状态为空闲或者不为空闲但是是当前线程持有锁,返回true表示抢锁成功
            return nonfairTryAcquire(acquires);
        }
    }AbstractQueuedSynchronizer#acquire
    public final void acquire(int arg) {
    //当前同步状态非空闲,并且是其他线程持有锁 返回false
        if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
}

通过代码可知:很多方法,trylock,unlock都是在父类sync实现

非公平性锁抢锁流程:
1、直接通过CAS操作抢锁,如果不成功进入常规抢锁流程。
2、获取当前锁的状态(state是否为0),如果为0表示空闲,直接通过CAS抢锁,如果成功,记录线程信息到锁上。
3、如果锁不为空闲且是当前线程持有锁,则可直接获取锁(state+1)。


重入锁的实现:
ReentrantLock都是将具体实现委托给内部类(SyncNonFairSyncFairSync)。
ReentrantLock重入次数是使用AQS的state属性,state大于0表示锁被占用(值表示当前线程重入次数),等于0表示锁空闲,小于0则表示重入次数太多导致溢出了。
可重入锁需要一个重入计数的变量,初始值为0,当成功请求锁加1,释放锁时减1,当释放锁时计数为0则真正释放锁,重入锁必须持有对锁持有者的引用,用以判断是否可以重入。

(3)Condition

synchronized与wait、notify、notifyAll方法结合可以实现等待/通知模式。reentantlock同样可以实现等待、通知模式,需要借助于Condition对象,具有更好的灵活性

newCondition方法

public Condition newCondition()

Condition中提供的方法如下:
在这里插入图片描述
awaitXXX和Object中的wait方法类似,使当前线程进入休眠等待,
signal和Object中的notify方法类似,唤醒一个处于休眠状态的线程
signalAll和Object中的signalAll方法类似,唤醒所有处于休眠状态的线程。


生产者和消费者

生产者

public class Producer extends Thread {
    private LinkedList<Integer> cap;//共享仓库
    private Random random = new Random();

    private ReentrantLock rlk;
    private Condition pToc;
    private Condition cTop;

    public Producer(LinkedList<Integer> cap, ReentrantLock rlk, Condition pToc, Condition cTop) {
        this.cap = cap;
        this.rlk = rlk;
        this.pToc = pToc;
        this.cTop = cTop;
    }

    @Override
    public void run() {
        while (true) {
            rlk.lock();
            try {
                if (cap.size() == 3) {//缓冲区满 生产者进行阻塞
                    System.out.println("缓冲区满");
                    cTop.await();
                }

                //生产产品
                int i = random.nextInt(1000);
                System.out.println("生产者生产了" + i);
                cap.add(i);

                //通知消费者消费产品
                pToc.signal();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            rlk.unlock();
        }
    }
}

消费者

public class Consumer extends Thread {
    private LinkedList<Integer> cap;//共享仓库

    private ReentrantLock rlk;
    private Condition pToc;
    private Condition cTop;

    public Consumer(LinkedList<Integer> cap, ReentrantLock rlk, Condition pToc, Condition cTop) {
        this.cap = cap;
        this.rlk = rlk;
        this.pToc = pToc;
        this.cTop = cTop;
    }

    @Override
    public void run() {
        while (true) {
            rlk.lock();
            try {
                if (cap.size() == 0) { //如果缓冲区为0,消费者阻塞
                    System.out.println("缓冲区为空");
                    pToc.await();
                }

                //消费者消费产品
                Integer i = cap.remove();
                System.out.println("消费者消费了" + i);

                //通知生产者生产
                cTop.signal();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            rlk.unlock();

        }
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        LinkedList<Integer> cap = new LinkedList<>();
        ReentrantLock reentrantLock = new ReentrantLock();
        //生产者通知消费者
        Condition pToc = reentrantLock.newCondition();
        //消费者通知生产者
        Condition cTop = reentrantLock.newCondition();

        Producer producer = new Producer(cap,reentrantLock,pToc,cTop);
        Consumer consumer = new Consumer(cap,reentrantLock,pToc,cTop);

        producer.start();
        consumer.start();
    }
}

执行结果
在这里插入图片描述
● 在调用Condition中的await或者是signal这些方法中任何的方法时,必须持有锁(ReentantLock),如果没有持有此锁,则抛出IllegalMonitorStateException异常。
● 在调用await方法时,将释放掉锁,并在这些方法返回之前,重新先获取该锁,才能执行。
● 如果线程在等待中被中断,则等待将终止,并抛出InterruptedException,清除掉中断状态。
● 等待状态的线程按照FIFO顺序接收信号。
● 等待方法返回的线程重新获取锁的顺序与线程最初获取锁的顺序是相同的。

循环打印ABC

ABCThread类

public class ABCThread extends Thread {
    private String name;
    private ReentrantLock rtl;
    private Condition waitc;//等待Condition
    private Condition sigalc; //通知Condition

    public ABCThread(String name,ReentrantLock rtl,Condition wc,Condition sc){
        this.name = name;
        this.rtl = rtl;
        this.waitc = wc;
        this.sigalc = sc;
    }

    @Override
    public void run() {
        int num =0;
        while (true) {
            rtl.lock();
            //等待其他线程通知,
            try {
                waitc.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //打印当前线程信息
            System.out.println(name);

            //通知下一个线程
            sigalc.signal();
            ++num;
            if (num >= 10) break;
            rtl.unlock();
        }
    }
}

测试

@Test
public void test() {
    ReentrantLock reentrantLock = new ReentrantLock();
    //A通知B
    Condition ab = reentrantLock.newCondition();
    //B通知C
    Condition bc = reentrantLock.newCondition();
    //C通知A
    Condition ca = reentrantLock.newCondition();


    new ABCThread("A", reentrantLock, ca, ab).start();
    new ABCThread("B", reentrantLock, ab, bc).start();
    new ABCThread("C", reentrantLock, bc, ca).start();
    //先发起通知A线程
    reentrantLock.lock();
    ca.signal();
    reentrantLock.unlock();
}

执行结果
在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ReentrantLockJava中的一个可重入锁,它提供了与synchronized关键字类似的功能,但更加灵活和强大。下面是ReentrantLock的使用介绍: 1. 创建ReentrantLock对象: ```java ReentrantLock lock = new ReentrantLock(); ``` 2. 获取锁: ```java lock.lock(); // 获取锁,如果锁已被其他线程持有,则当前线程会被阻塞,直到获取到锁为止 ``` 3. 释放锁: ```java lock.unlock(); // 释放锁,如果当前线程持有锁,则释放锁;如果当前线程没有持有锁,则会抛出IllegalMonitorStateException异常 ``` 4. 使用try-finally确保锁的释放: ```java lock.lock(); try { // 执行需要同步的代码块 } finally { lock.unlock(); // 在finally块中释放锁,确保锁的释放 } ``` 5. 使用条件变量: ```java Condition condition = lock.newCondition(); // 创建条件变量 condition.await(); // 当前线程等待,直到其他线程调用signal或signalAll方法唤醒它 condition.signal(); // 唤醒一个等待的线程 condition.signalAll(); // 唤醒所有等待的线程 ``` 6. 公平锁和非公平锁: ReentrantLock可以是公平锁或非公平锁,默认情况下是非公平锁。在构造ReentrantLock对象时,可以传入一个boolean参数来指定是否使用公平锁: ```java ReentrantLock lock = new ReentrantLock(true); // 使用公平锁 ReentrantLock lock = new ReentrantLock(false); // 使用非公平锁 ``` 7. 其他方法: - `isHeldByCurrentThread()`:判断当前线程是否持有锁。 - `getHoldCount()`:获取当前线程持有锁的次数。 - `getQueueLength()`:获取等待获取锁的线程数。 - `hasQueuedThreads()`:判断是否有线程在等待获取锁。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WYSCODER

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值