java并发编程JUC并发包之ReentrantLock


写在前面,JUC基础知识,详见 JUC并发包核心之AbstractQueuedSynchronizer
java并发编程面试必备,详见

  • java并发编程面试必备

概述

1、轻量级锁
2、可重入(通过state计数)、可中断、可限时
3、支持公平锁和非公平锁
4、支持唤醒指定线程——condition

成员属性及构造方法

public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    /** Synchronizer providing all implementation mechanics */
    private final Sync sync;

    public ReentrantLock() {
    	//默认非公平锁
        sync = new NonfairSync();
    }

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

内部类

Sync类

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

        /**
         * Performs {@link Lock#lock}. The main reason for subclassing
         * is to allow fast path for nonfair version.
         */
        abstract void lock();

		//非公平方式尝试获取锁
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
            	//state为0时,直接cas,成功就设置独占
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
            	//state不为0,如果是当前线程持有,则state+1
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            //state不为0,且不是当前线程持有,返回false
            return false;
        }

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

NonfairSync类

只要记住,非公平就是先cas获取state,如果成功,相当于插队成功,也就是非公平;如果失败,老老实实排队

static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
		
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
            	//详见AQS那篇文章中的acquire方法,主要做了3件事:
            	//1、调用实现类的tryAcquire();2、addWaiter()入队;3、acquireQueued()公平获取锁
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

FairSync类

公平就是先看有没有人排队,没人排队就cas获取state,有人排队就跟在尾部排队

static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

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

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        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)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

常用方法(函数)

在这里插入图片描述
普通锁:

    public void lock() {
        sync.lock();
    }
// 尝试获取锁,立即返回获取结果 轮询锁
    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }
//尝试获取锁,最多等待 timeout 时长 超时锁
    public void unlock() {
        sync.release(1);
    }
//可中断锁,调用线程 interrupt 方法,则锁方法抛出 InterruptedException  中断锁
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

Condition接口

Condition与重入锁是通过lock.newCondition()方法产生一个与当前重入锁绑定的Condtion实例,我们通知该实例来控制线程的等待与通知。
阻塞队列JUC并发包之阻塞队列LinkedBlockingQueue、LinkedBlockingDeque和ArrayBlockingQueue,就是通过Condition来实现生产者消费者模式的。

接口中方法

public interface Condition {
     //使当前线程加入 await() 等待队列中,并释放当锁,当其他线程调用signal()会重新请求锁。与Object.wait()类似。
    void await() throws InterruptedException;

    //调用该方法的前提是,当前线程已经成功获得与该条件对象绑定的重入锁,否则调用该方法时会抛出IllegalMonitorStateException。
    //调用该方法后,结束等待的唯一方法是其它线程调用该条件对象的signal()或signalALL()方法。等待过程中如果当前线程被中断,该方法仍然会继续等待,同时保留该线程的中断状态。 
    void awaitUninterruptibly();

    // 调用该方法的前提是,当前线程已经成功获得与该条件对象绑定的重入锁,否则调用该方法时会抛出IllegalMonitorStateException。
    //nanosTimeout指定该方法等待信号的的最大时间(单位为纳秒)。若指定时间内收到signal()或signalALL()则返回nanosTimeout减去已经等待的时间;
    //若指定时间内有其它线程中断该线程,则抛出InterruptedException并清除当前线程的打断状态;若指定时间内未收到通知,则返回0或负数。 
    long awaitNanos(long nanosTimeout) throws InterruptedException;

    //与await()基本一致,唯一不同点在于,指定时间之内没有收到signal()或signalALL()信号或者线程中断时该方法会返回false;其它情况返回true。
    boolean await(long time, TimeUnit unit) throws InterruptedException;

   //适用条件与行为与awaitNanos(long nanosTimeout)完全一样,唯一不同点在于它不是等待指定时间,而是等待由参数指定的某一时刻。
    boolean awaitUntil(Date deadline) throws InterruptedException;
    
    //唤醒一个在 await()等待队列中的线程。与Object.notify()相似
    void signal();

   //唤醒 await()等待队列中所有的线程。与object.notifyAll()相似
    void signalAll();
}

condition实现生产者消费者

import sun.awt.windows.ThemeReader;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class TestReentrantLockCondition<E> {
    //面试题2
    //固定同步容器  消费者和生产者

    private LinkedList<E> list = new LinkedList<>();
    private static final int MAX = 10;
    private int count = 0;

    private ReentrantLock reentrantLock = new ReentrantLock();
    private Condition producer = reentrantLock.newCondition();
    private Condition consumer = reentrantLock.newCondition();

    public void put(E e) {
        try {
            reentrantLock.lock();
            while (count == MAX) {
                System.out.println(Thread.currentThread().getName() + " await()");
                producer.await();
            }
            count++;
            list.add(e);
            System.out.println(Thread.currentThread().getName() + " add " + e);
            consumer.signalAll();
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        } finally {
            reentrantLock.unlock();
        }
    }

    public E get() {
        E e = null;
        try {
            reentrantLock.lock();
            while (count == 0) {
                System.out.println(Thread.currentThread().getName() + " await()");
                consumer.await();
            }
            count--;
            e = list.removeFirst();
            System.out.println(Thread.currentThread().getName() + " remove " + e);
            producer.signalAll();
        } catch (InterruptedException e2) {
            e2.printStackTrace();
        } finally {
            reentrantLock.unlock();
        }
        return e;
    }

    public int getCount() {
        return count;
    }

    public static void main(String[] args) {
        TestReentrantLockCondition<String> testReentrantLockCondition = new TestReentrantLockCondition();
        CountDownLatch countDownLatch = new CountDownLatch(20);
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 5; j++) {
                    System.out.println(Thread.currentThread().getName() + " consume: " + testReentrantLockCondition.get());
                }
                countDownLatch.countDown();
            }, "c" + i).start();
        }
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 5; j++) {
                    testReentrantLockCondition.put(Thread.currentThread().getName() + " " + j);
                }
                countDownLatch.countDown();

            }, "p" + i).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("mian end,count: " + testReentrantLockCondition.getCount());

    }
}

condition实现交替输出A1B2C3

全网很多这题的代码,但是大多数循环n次,可能出现1A2B3C的顺序,或者AB1C2之类的顺序错误。可以通过CountDownLatch控制哪个线程先执行。

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

public class Test_A1B2C3_ReentranLock_condition {
    static volatile int i = 0;

    public static void main(String[] args) {
        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        for (int j = 0; j < 10000; j++) {
            Lock lock = new ReentrantLock();
            
            //控制哪个线程先执行
            CountDownLatch count = new CountDownLatch(1);
            
            //控制线程一起结束,类似于实现join()的功能,这个方式用起来比较简单
            CountDownLatch countDownLatch = new CountDownLatch(14);
            
            Condition condition = lock.newCondition();
            new Thread(() -> {
                try {
                	//数字线程先阻塞
                    count.await();
                    lock.lock();
                    for (char c : aI) {
                        System.out.print(c);
                        countDownLatch.countDown();
                        condition.signal();
                        condition.await();
                    }
                    condition.signal();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }, "t1").start();
            new Thread(() -> {
                try {
                    lock.lock();
                    //字母线程获取到锁之后,countDown(),保证字母线程先执行
                    count.countDown();
                    for (char c : aC) {
                        System.out.print(c);
                        countDownLatch.countDown();
                        condition.signal();
                        condition.await();
                    }

                    condition.signal();

                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }

            }, "t2").start();
            try {
            	//等待所有线程一起执行完,再执行后面流程(类似于join())
                countDownLatch.await();
                System.out.println();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值