ReentrantLock 可重入锁 --06

ReentrantLock

相对于 synchronized

  • 可中断
  • 可以设置超时时间
  • 可以设置公平锁 (防止饥饿)
  • 支持多个条件变量 (类似于支持多个 waitSet)
    与synchronized一样 ,都支持可重入

基本语法

 ReentrantLock reentrantLock = new ReentrantLock();
        reentrantLock.lock();
        try
        {
            //临界区
        }finally {
          reentrantLock.unlock();
        }

可重入锁

可重入是指同一个线程如果首个获取了这把锁,那么因为他是这把锁的拥有者,因此有权利再次获取这把锁
如果不是可重入锁,那么第二次获取锁,自己也会被挡住

 private static  ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        lock.lock();    //lock加锁是不可打断的
        try {
            log.info("main ");
            m1();
        }finally {
            lock.unlock();
        }
    }
    static void m1(){
        lock.lock();
        try {
            log.info("m1 ");
            m2();
        }finally {
            lock.unlock();
        }
    }
    static void m2(){
        lock.lock();
        try {
            log.info("m2");
        }finally {
            lock.unlock();
        }
    }

可打断

被动的被打断 避免死等

private static ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            // 没有竞争获取锁
            //有竞争进入阻塞队列,可以被其他线程用 interruput 打断
            try {
                log.info("尝试获得锁");
                lock.lockInterruptibly();   //可打断锁   防止无限制等待 ,可以防止死锁
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.info("没有获得锁,返回");
                return;
            }
            try {
                log.info("获取到锁");
            } finally {
                lock.unlock();
            }
        }, "t1");
        lock.lock();
        t1.start();
        Thread.sleep(1000);
        log.info("打断t1");
        t1.interrupt();
    }

锁超时

 private static ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            //if(!lock.tryLock())  //尝试获得锁  成功获得锁 失败不去阻塞队列等待   防止无限制等待
            try {
                if(!lock.tryLock(2,TimeUnit.SECONDS))   //设置等待时间   也支持可打断的特性
                {
                    log.info("获取不到锁");
                    return;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.info("没有获取锁,返回");
                return;
            }
            try {
                log.info("获取到锁");
            }finally {
                lock.unlock();
            }
        }, "t1");
        lock.lock();
        t1.start();
    }

公平锁

RenntranLock 默认是不公平锁
公平锁 可以解决饥饿问题 , 设置公平锁会降低并发度

public ReentrantLock(boolean fair)  构造函数传入是否是公平锁   

条件变量

synchronized 中也有条件变量,就是waitSet休息室,当条件不满足时进入waitSet等待

ReentranLock的条件变量比synchronized强大之处在于,支持多个条件变量

  • synchronized 时那些不满足条件的线程都在一间休息室等消息
  • 而ReentranLock支持多间休息室,可以分类

使用流程

  • await 前需要获取锁
  • await 执行后,会释放锁,静茹conditionObject等待
  • await 的线程被唤醒(或打断,超时)重新竞争lock锁
  • 竞争lock锁成功后,从await后继续执行
 static  ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        //创建一个姓的条件变量 (休息室)
        Condition condition1 = lock.newCondition();
        Condition condition2 = lock.newCondition();
        lock.lock();
        //进入休息室等待
        condition1.await();
        condition1.signal();  //唤醒等待的线程
        condition2.signalAll(); // 唤醒所有等待的线程
    }

同步线程之顺序控制

  1. 使用synchrinized wait() notifyAll()
    static final  Object lock = new Object();
    static boolean canRun = false;
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (lock)
            {
                while (!canRun)  //防止虚假唤醒
                {
                    try {
                        lock.wait();  // 不允许打印时 等待
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.info("1");
            }
        }, "t1");
        Thread thread2 = new Thread(() -> {
            synchronized (lock)
            {
                log.info("2");
                canRun = true; //设置允许打印
                lock.notifyAll(); //唤醒所有等待线程
            }
        }, "t2");
        thread1.start();
        thread2.start();
    }
  1. 使用RenntrinLock
 static final ReentrantLock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();
    static boolean canRun = false;
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {

            try {
                while (!canRun && lock.tryLock())  //尝试获得锁 
                {
                    condition.await();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
            log.info("1");

        }, "t1");
        Thread thread2 = new Thread(() -> {
            lock.lock();
            try {
                log.info("2");
                canRun = true;
                condition.signal();
            }finally {
                lock.unlock();
            }
        }, "t2");

        thread1.start();
        thread2.start();
    }
  1. 使用park 和 unpark
  static boolean canRun = false;
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            while (!canRun)
            {
                LockSupport.park();
            }
            log.info("1");

        }, "t1");

        Thread thread2 = new Thread(() -> {
            log.info("2");
            canRun = true;
            LockSupport.unpark(thread1);
        }, "t2");

        thread1.start();
        thread2.start();
    }

同步线程之交替输出

三个线程 t1输出 a t2输出 b t3输出c 让他们交替输出 abcabcabcabcabc 每个线程输出五次

  1. 使用synchrinized wait() notifyAll()
    public static void main(String[] args) {
        WaitNatify waitNatify = new WaitNatify(1,5);
        Thread thread1 = new Thread(() -> {
            waitNatify.print("a",1,2);
        }, "t1");

        Thread thread2 = new Thread(() -> {
            waitNatify.print("b",2,3);
        }, "t2");

        Thread thread3 = new Thread(() -> {
            waitNatify.print("c",3,1);
        }, "t3");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}
class WaitNatify{
    private int flag;
    private int loopNum;
    public WaitNatify(int flag, int loopNum) {
        this.flag = flag;
        this.loopNum = loopNum;
    }
    public void print(String str,int waitFlag,int nextFlag) {
        for (int i = 0; i < loopNum; i++) {
            synchronized (this)
            {
                while (flag != waitFlag)
                {
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.printf(str);
                flag = nextFlag;
                this.notifyAll();
            }
        }
    }
}
  1. 使用ReentrinLock
 public static void main(String[] args) throws InterruptedException {
        AwaitSignalAll awaitSignalAll = new AwaitSignalAll(5);
        Condition a = awaitSignalAll.newCondition();
        Condition b = awaitSignalAll.newCondition();
        Condition c = awaitSignalAll.newCondition();

        Thread thread1 = new Thread(() -> {
            awaitSignalAll.print("a", a,b);
        }, "t1");

        Thread thread2 = new Thread(() -> {
            awaitSignalAll.print("b", b,c);
        }, "t2");

        Thread thread3 = new Thread(() -> {
            awaitSignalAll.print("c", c,a);
        }, "t3");
        thread1.start();
        thread2.start();
        thread3.start();

        Thread.sleep(1000);
        awaitSignalAll.lock();  // 这里不获得锁 引起java.lang.IllegalMonitorStateException异常 (非法监视器状态异常)
        try{
            a.signalAll();  //目前线程全部在休息室等待  , 这里设置 第一次被唤醒的休息室的线程 
        }finally {
            awaitSignalAll.unlock();
        }
    }
}

class AwaitSignalAll extends ReentrantLock {
    private int loopNum;
    public AwaitSignalAll(int loopNum) {
        this.loopNum = loopNum;
    }
    public void print(String str, Condition current,Condition next) {
        for (int i = 0; i < loopNum; i++) {
            lock();
            try {
                current.await();  //三个线程调用 全部进入自己的休息室等待
                System.out.printf(str);   //等待结束 打印str
                next.signalAll();  // 唤醒下一个应该执行的休息室内的线程
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                unlock();
            }

        }
    }
}
  1. 使用park 和 unpark
    static Thread thread1;
    static Thread thread2;
    static Thread thread3;
    public static void main(String[] args) throws InterruptedException {
        ParkUnpark parkUnpark = new ParkUnpark(5);
        thread1 = new Thread(() -> {
            parkUnpark.print("a", thread2);
        }, "t1");
        thread2 = new Thread(() -> {
            parkUnpark.print("b", thread3);
        }, "t2");
        thread3 = new Thread(() -> {
            parkUnpark.print("c", thread1);
        }, "t3");
        thread1.start();
        thread2.start();
        thread3.start();
        LockSupport.unpark(thread1);
    }
}
class ParkUnpark {
    private int loopNum;
    public ParkUnpark(int loopNum) {
        this.loopNum = loopNum;
    }
    public void print(String str, Thread next) {
        for (int i = 0; i < loopNum; i++) {
            LockSupport.park();
            System.out.printf(str);
            LockSupport.unpark(next);
        }

    }
}

AQS AbstractQueuedSynchronizer

全称是 AbstractQueuedSynchronizer,是阻塞式锁和相关的同步器工具的框架

  • 用 state 属性来表示资源的状态(分独占模式和共享模式),子类需要定义如何维护这个状态,控制如何获取锁和释放锁
    • getState - 获取 state 状态
    • setState - 设置 state 状态
    • compareAndSetState - cas 机制设置 state 状态
    • 独占模式是只有一个线程能够访问资源,而共享模式可以允许多个线程访问资源
  • 提供了基于 FIFO 的等待队列,类似于 Monitor 的 EntryList
  • 条件变量来实现等待、唤醒机制,支持多个条件变量,类似于 Monitor 的 WaitSet

子类主要实现这样一些方法(默认抛出 UnsupportedOperationException)

  • tryAcquire //获取锁
  • tryRelease // 释放锁
  • tryAcquireShared
  • tryReleaseShared
  • isHeldExclusively
// 如果获取锁失败
if (!tryAcquire(arg)) {
 // 入队, 可以选择阻塞当前线程    实际用了 park unpark 方法
}
// 如果释放锁成功
if (tryRelease(arg)) {
 // 让阻塞线程恢复运行
}

不可重入锁

public static void main(String[] args) {
        Mylock mylock = new Mylock();
        new Thread(() -> {
            mylock.lock();
            try {
                System.out.println("枷锁成功");
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                mylock.unlock();
                System.out.println("解锁成功");
            }
        }).start();

        new Thread(() -> {
            mylock.lock();
            try {
                System.out.println("枷锁成功");
            }finally {
                mylock.unlock();
                System.out.println("解锁成功");
            }
        }).start();
    }
}

//   自定义不可重入锁
class Mylock implements Lock {

    //同步器类   独占锁
    class  MySync  extends AbstractQueuedLongSynchronizer{
        @Override  // 获取锁
        protected boolean tryAcquire(long arg) {
            if(compareAndSetState(0,1))
            {
                // 加锁成功  设置线程位当前线程
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        @Override  //释放锁
        protected boolean tryRelease(long arg) {

            setExclusiveOwnerThread(null);
            setState(0);  // state 是volatile 修饰,具有写屏障 保证之前的变量可见性
            return true;

        }

        @Override  //是否持有独占锁
        protected boolean isHeldExclusively() {
            return  getState() == 1;
        }
                // 创建条件变量
        public Condition newCondition(){
            return  new ConditionObject();
        }
    }

    private MySync  sync = new MySync();

    @Override       // 加锁   (不成功进入等待队列 )
    public void lock() {
        sync.acquire(1);
    }

    @Override      // 可打断锁
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    @Override       // 可打断锁  尝试一次
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }

    @Override       //  尝试加锁 (带超时时间)
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1,unit.toNanos(time));
    }

    @Override       // 解锁
    public void unlock() {
        sync.release(1);
    }

    @Override       //  创建条件变量
    public Condition newCondition() {
        return sync.newCondition();
    }
}

ReentrinLock原理

构造器

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

// 加锁方法

 final void lock() {
            if (compareAndSetState(0, 1))   // 改变 锁状态为加锁
                setExclusiveOwnerThread(Thread.currentThread());  //更改锁线程为当前线程
            else
                acquire(1);   // 加锁失败
        }

在这里插入图片描述


 public final void acquire(int arg) {
 		// 尝试加锁                  放入阻塞队列  addWaiter  ()
        if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

尝试加锁
在这里插入图片描述
acquireQueued

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {    // 死循环 尝试获得锁
                final Node p = node.predecessor();   //前驱节点   
                if (p == head && tryAcquire(arg)) {   // 如果前驱节点是头节点  说明头节点是第二的 才有资格尝试获得锁
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&   // 尝试获取锁失败
                    parkAndCheckInterrupt())   //阻塞 
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

在这里插入图片描述
释放锁

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

1999

每人一点点,明天会更好

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

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

打赏作者

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

抵扣说明:

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

余额充值