文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

java ReentrantLock并发锁使用详解

2022-11-13 18:18

关注

一、ReentrantLock是什么

ReentrantLock是一种基于AQS框架的应用实现,是JDK中的一种线程并发访问的同步手段,它的功能类似于synchronized是一种互斥锁,可以保证线程安全。

相对于 synchronized, ReentrantLock具备如下特点:

进入源码可以看到,其实现了公平锁和非公平锁

内部实现了加锁的操作,并且支持重入锁。不用我们再重写

解锁操作

1-1、ReentrantLock和synchronized区别

synchronized和ReentrantLock的区别:

1-2、ReentrantLock的使用

1-2-1、ReentrantLock同步执行,类似synchronized

使用ReentrantLock需要注意的是:一定要在finally中进行解锁,方式业务抛出异常,无法解锁

public class ReentrantLockDemo {
    private static  int sum = 0;
    private static ReentrantLock lock=new ReentrantLock();
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 3; i++) {
            Thread thread = new Thread(()->{
                //加锁
                lock.lock();
                try {
                    // 临界区代码
                    // TODO 业务逻辑:读写操作不能保证线程安全
                    for (int j = 0; j < 10000; j++) {
                        sum++;
                    }
                } finally {
                    // 解锁--一定要在finally中解锁,防止业务代码异常,无法释放锁
                    lock.unlock();
                }
            });
            thread.start();
        }
        Thread.sleep(2000);
        System.out.println(sum);
    }
}

测试结果:

1-2-2、可重入锁

可重入锁就是 A(加锁)-->调用--->B(加锁)-->调用-->C(加锁),从A到C即使B/C都有加锁,也可以进入

@Slf4j
public class ReentrantLockDemo2 {
    public static ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        method1();
    }
    public static void method1() {
        lock.lock();
        try {
            log.debug("execute method1");
            method2();
        } finally {
            lock.unlock();
        }
    }
    public static void method2() {
        lock.lock();
        try {
            log.debug("execute method2");
            method3();
        } finally {
            lock.unlock();
        }
    }
    public static void method3() {
        lock.lock();
        try {
            log.debug("execute method3");
        } finally {
            lock.unlock();
        }
    }
}

执行结果:

1-2-3、锁中断

可以使用lockInterruptibly来进行锁中断

lockInterruptibly()方法能够中断等待获取锁的线程。当两个线程同时通过lock.lockInterruptibly()获取某个锁时,假若此时线程A获取到了锁,而线程B只有等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。

public class ReentrantLockDemo3 {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        Thread t1 = new Thread(() -> {
            log.debug("t1启动...");
            try {
                lock.lockInterruptibly();
                try {
                    log.debug("t1获得了锁");
                } finally {
                    lock.unlock();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.debug("t1等锁的过程中被中断");
            }
        }, "t1");
        lock.lock();
        try {
            log.debug("main线程获得了锁");
            t1.start();
            //先让线程t1执行
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            t1.interrupt();
            log.debug("线程t1执行中断");
        } finally {
            lock.unlock();
        }
    }
}

执行结果:

1-2-4、获得锁超时失败

可以让线程等待指定的时间,如果还未获取锁则进行失败处理。

如下代码,首先让主线程获得锁,然后让子线程启动尝试获取锁,但是由于主线程获取锁之后,让线程等待了2秒,而子线程获得锁的超时时间只有1秒,如果未获得锁,则进行return失败处理

public class ReentrantLockDemo4 {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        Thread t1 = new Thread(() -> {
            log.debug("t1启动...");
            //超时
            try {
                if (!lock.tryLock(1, TimeUnit.SECONDS)) {
                    log.debug("等待 1s 后获取锁失败,返回");
                    return;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                return;
            }
            try {
                log.debug("t1获得了锁");
            } finally {
                lock.unlock();
            }
        }, "t1");
        lock.lock();
        try {
            log.debug("main线程获得了锁");
            t1.start();
            //先让线程t1执行
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } finally {
            lock.unlock();
        }
    }
}

执行结果:

1-2-5、公平锁

ReentrantLock 默认是不公平的

首先启动500次for循环创建500个线程,然后进行加锁操作,并同时启动了。这样这500个线程就依次排队等待加锁的处理

下面500个线程也是等待加锁操作

如果使用公平锁,下面500的线程只有等上面500个线程运行完成之后才能获得锁。

@Slf4j
public class ReentrantLockDemo5 {
    public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock(true); //公平锁
        for (int i = 0; i < 500; i++) {
            new Thread(() -> {
                lock.lock();
                try {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    log.debug(Thread.currentThread().getName() + " running...");
                } finally {
                    lock.unlock();
                }
            }, "t" + i).start();
        }
        // 1s 之后去争抢锁
        Thread.sleep(1000);
        for (int i = 0; i < 500; i++) {
            new Thread(() -> {
                lock.lock();
                try {
                    log.debug(Thread.currentThread().getName() + " running...");
                } finally {
                    lock.unlock();
                }
            }, "强行插入" + i).start();
        }
    }
}

测试结果(后进入的线程都在等待排队)

使用非公平锁的情况下,就可以看到下面500线程有些线程就可以抢占锁了

那ReentrantLock为什么默认使用非公平锁呢?实际上就是为了提高性能,如果使用公平锁,当前锁对象释放之后,还需要去队列中获取第一个排队的线程,然后进行加锁处理。而非公平锁,可能再当前对象释放锁之后,正好有新的线程在获取锁,这样就可以直接进行加锁操作,不必再去队列中读取。

以上就是java ReentrantLock并发锁使用详解的详细内容,更多关于java ReentrantLock并发锁的资料请关注编程网其它相关文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯