Java显式锁详情

 更新时间:2021年09月10日 09:39:08   作者:IT王小二  
这篇文章主要详细的介绍了shenJava显式锁常用的api及标准用法,感兴趣的朋友,需要的朋友可以参考下面文章里的内容

Java显式锁

一、显式锁

什么是显式锁?

由自己手动获取锁,然后手动释放的锁。

有了 synchronized(内置锁) 为什么还要 Lock(显示锁)?

使用 synchronized 关键字实现了锁功能的,使用 synchronized 关键字将会隐式地获取锁,但是它将锁的获取和释放固化了,也就是先获取再释放。

与内置加锁机制不同的是,Lock 提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有加锁和解锁的方法都是显式的。

二、Lock的常用api

方法名称 描述
void lock() 获取锁
void lockInterruptibly() throws InterruptedException 可中断的获取锁,和lock()方法的不同之处在于该方法会响应中断,即在锁的获取中可以中断当前线程
boolean tryLock() 尝试非阻塞的获取锁,调用该方法后立刻返回,如果能够获取则返回true,否则返回false
boolean tryLock(long time, TimeUnit unit) throws InterruptedException 超时获取锁,当前线程会在以下三种情况下会返回:
1. 当前线程在超时时间内获得了锁
2.当前线程在超市时间内被中断
3. 超时时间结束,返回false
void unlock(); 释放锁

三、Lock的标准用法

lock.lock();
try {
    // 业务逻辑
} finally {
    lock.unlock();
}

  • finally 块中释放锁,目的是保证在获取到锁之后,最终能够被释放。
  • 不要将获取锁的过程写在 try 块中,因为如果在获取锁(自定义锁的实现)时发生了异常,异常抛出的同时,也会导致锁无故释放。

四、ReentrantLock(可重入锁)

Lock接口常用的实现类是 ReentrantLock。

示例代码:主线程100000次减,子线程10万次加。

public class ReentrantLockTest {

    private Lock lock = new ReentrantLock();
    private int count = 0;

    public int getCount() {
        return count;
    }

    private static class ReentrantLockThread extends Thread {
        private ReentrantLockTest reentrantLockTest;

        public ReentrantLockThread(ReentrantLockTest reentrantLockTest) {
            this.reentrantLockTest = reentrantLockTest;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100000; i++) {
                reentrantLockTest.incr();
            }
            System.out.println(Thread.currentThread().getName() + " end, count =  " + reentrantLockTest.getCount());
        }
    }

    private void incr() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    private void decr() {
        lock.lock();
        try {
            count--;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ReentrantLockTest reentrantLockTest = new ReentrantLockTest();
        new ReentrantLockThread(reentrantLockTest).start();

        for (int i = 0; i < 100000; i++) {
            // 递减100000
            reentrantLockTest.decr();
        }
        System.out.println(Thread.currentThread().getName() + " count =  " + reentrantLockTest.getCount());
    }
}

1. 锁的可重入性

简单地讲就是:“同一个线程对于已经获得到的锁,可以多次继续申请到该锁的使用权”。而 synchronized 关键字隐式的支持重进入,比如一个 synchronized 修饰的递归方法,在方法执行时,执行线程在获取了锁之后仍能连续多次地获得该锁

同样,ReentrantLock 在调用 lock()方法时,已经获取到锁的线程,能够再次调用lock()方法获取锁而不被阻塞

2. 公平锁和非公平锁

  • 如果在时间上,先对锁进行获取的请求一定先被满足,那么这个锁是公平的,反之,是不公平的。公平的获取锁,也就是等待时间最长的线程最优先获取锁,也可以说锁获取是顺序的。
  • ReentrantLock 提供了一个构造函数,能够控制锁是否是公平的(缺省为不公平锁)。事实上,公平的锁机制往往没有非公平的效率高。
  • 在激烈竞争的情况下,非公平锁的性能高于公平锁的性能的一个原因是:在恢复一个被挂起的线程与该线程真正开始运行之间存在着严重的延迟。
  • 假设线程 A 持有一个锁,并且线程 B 请求这个锁,由于这个锁已被线程 A 持有,因此 B 将被挂起。当 A 释放锁时,B 将被唤醒,因此会再次尝试获取锁。与此同时,如果 C 也请求这个锁,那么 C 很可能会在 B 被完全唤醒之前获得、使用以及释放这个锁,这样的情况是一种“双赢”的局面:B 获得锁的时刻并没有推迟,C 更早地获得了锁,完成了自己的任务,然后释放了锁,并且吞吐量也获得了提高。

五、ReentrantReadWriteLock(读写锁)

ReentrantReadWriteLock 是 ReadWriteLock 的实现类。

之前提到锁基本都是排他锁,这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时刻可以允许多个读线程访问但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大提升。

读锁不排斥读锁,但是排斥写锁;写锁即排斥读锁也排斥写锁。

private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock getLock = lock.readLock(); // 读锁
private final Lock setLock = lock.writeLock(); // 写锁


至于上锁、解锁与 ReentrantLock 使用方式一致。

六、Condition

  • 任意一个 Java 对象,都拥有一组监视器方法(定义在 java.lang.Object 上),主要包括 wait()、wait(long timeout)、notify()以及 notifyAll()方法,这些方法与 synchronized 同步关键字配合,可以实现等待/通知模式。
  • Condition 接口也提供了类似 Object 的监视器方法,与 Lock 配合可以实现等待/通知模式。

常用api

方法名称 描述
void await() throws InterruptedException 使当前线程进入等待状态直到被通知(signal)或中断
void signal() 唤醒一个等待的线程
void signalAll() 唤醒所有等待的线程

示例代码,主线程调用方法唤醒两个子线程。

public class ConditionTest {

    private volatile boolean flag = false;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    private void task1() {
        lock.lock();
        try {
            try {
                System.out.println(Thread.currentThread().getName() + " 等待中");
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " 等待结束");
            System.out.println("发送邮件");
        } finally {
            lock.unlock();
        }
    }

    private void task2() {
        lock.lock();
        try {
            try {
                System.out.println(Thread.currentThread().getName() + " 等待中");
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " 等待结束");
            System.out.println("发送短信");
        } finally {
            lock.unlock();
        }
    }

    private void updateFlag() {
        lock.lock();
        try {
            this.flag = true;
            this.condition.signalAll();
        }finally {
            lock.unlock();
        }
    }

    private static class ConditionThread1 extends Thread {
        private ConditionTest conditionTest;
        public ConditionThread1(ConditionTest conditionTest) {
            this.conditionTest = conditionTest;
        }

        @Override
        public void run() {
            if (!conditionTest.flag) {
                conditionTest.task1();
            }
        }
    }

    private static class ConditionThread2 extends Thread {
        private ConditionTest conditionTest;
        public ConditionThread2(ConditionTest conditionTest) {
            this.conditionTest = conditionTest;
        }

        @Override
        public void run() {
            if (!conditionTest.flag) {
                conditionTest.task2();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ConditionTest conditionTest = new ConditionTest();
        new ConditionThread1(conditionTest).start();
        new ConditionThread2(conditionTest).start();
        Thread.sleep(1000);
        System.out.println("flag 改变。。。");
        conditionTest.updateFlag();
    }
}

以上就是Java显式锁详情的详细内容,更多关于Java显式锁的资料请关注脚本之家其它相关文章!

相关文章

  • Java内存溢出的几个区域总结(注意避坑!)

    Java内存溢出的几个区域总结(注意避坑!)

    内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能提供的最大内存,下面这篇文章主要给大家介绍了关于Java内存溢出的几个区域,总结出来给大家提醒注意避坑,需要的朋友可以参考下
    2022-11-11
  • Java设计模式七大原则之迪米特法则详解

    Java设计模式七大原则之迪米特法则详解

    迪米特法则要求一个对象应该对其他对象有最少的了解,所以迪米特法则又叫做最少知识原则。本文将为大家详细介绍Java设计模式七大原则之一的迪米特法则,需要的可以参考一下
    2022-02-02
  • java web验证码实现代码分享

    java web验证码实现代码分享

    这篇文章主要为大家分享了java web验证码的实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-06-06
  • Java while(scanner.hasNext())无法跳出的解决方案

    Java while(scanner.hasNext())无法跳出的解决方案

    这篇文章主要介绍了Java while(scanner.hasNext())无法跳出的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • Spring Boot jar可执行原理的彻底分析

    Spring Boot jar可执行原理的彻底分析

    这篇文章主要给大家介绍了关于Spring Boot jar可执行原理的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Spring Boot具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-07-07
  • MyBatis中 #{} 和 ${} 的区别小结

    MyBatis中 #{} 和 ${} 的区别小结

    MyBatis中#{}和${}是两种占位符,本文就来介绍一下MyBatis中 #{} 和 ${} 的区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-12-12
  • Java基础之重载(Overload)与重写(Override)详解

    Java基础之重载(Overload)与重写(Override)详解

    这篇文章主要介绍了Java基础之重载(Overload)与重写(Override)详解,文中有非常详细的代码示例,对正在学习java基础的小伙伴们有非常好的帮助,需要的朋友可以参考下
    2021-04-04
  • Java微信小程序医院挂号系统

    Java微信小程序医院挂号系统

    这篇文章主要介绍了基于Java微信小程序医院挂号系统,可以实现远程处理事务,远程提交工作和随时追踪工作的状态,文中提供了解决思路和部分实现代码,需要的朋友可以参考下
    2023-03-03
  • 浅谈Java中生产者与消费者问题的演变

    浅谈Java中生产者与消费者问题的演变

    这篇文章主要介绍了浅谈Java中生产者与消费者问题的演变,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-10-10
  • IntelliJ IDEA右键文件夹没有Java Class文件的原因及解决方法

    IntelliJ IDEA右键文件夹没有Java Class文件的原因及解决方法

    这篇文章主要介绍了IntelliJ IDEA右键文件夹没有Java Class文件的原因及解决方法,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09

最新评论