为了账号安全,请及时绑定邮箱和手机立即绑定

【九月打卡】第16天 不可不说的“锁”事【种类繁多,如何一一突破?】

标签:
Java

课程名称:玩转Java并发工具,精通JUC,成为并发多面手
课程章节:第5章 不可不说的“锁”事【种类繁多,如何一一突破?】
课程讲师: 悟空

课程内容

1.1、简介、地位、作用

  • 锁是一种工具,用于控制对共享资源的访问。
  • Lock和synchronized,这两个是最常见的锁,它们都可以达到线程安全的目的,但是在使用上和功能上又有较大的不同。
  • Lock并是用来代替synchronized的,而是当使用synchronized不合适或不足以满足需求的时候,来提供高级功能的。
  • Lock接口中最常见的实现类就是ReentrantLock
  • 通常情况下,Lock只允许一个线程来访问这个共享资源。不过有的时候,一些特殊的实现也可允许并发访问,比如ReadWriteLock里面的ReadLock

1.2、为什么synchronized不够用?为什么需要Lock?

1.2.1、为什么synchronized不够用?

  1. 效率低:锁的释放情况少(1.把锁的代码执行完毕;2.执行到一半发生异常了,JVM释放;除了wait方法能释放锁外,是没有办法释放锁的)、试图获得锁时不能设定超时、不能中断一个正在试图获得锁的线程
  2. 不够灵活(读写锁更灵活):加锁和释放锁的时机单一,每个锁仅有单一的条件(某个对象),可能是不够的
  3. 无法知道是否成功获取到锁

1.3、方法介绍

  • 在Lock中声明了个方法来获取锁
  • lock()、tryLock()、tryLock(long time,TimeUtil unit)和lockInterruptibly()

1.3.1、lock()

  • lock()就是最普通的获取锁。如果锁已被其他线程获取,则进行等待
  • Lock不会像synchronized一样在异常时自动释放锁
  • 因此最佳实践是,在finally中释放锁,以保证异常发生时锁一定被释放
  • lock()方法不能被中断,这会带来很大的隐患:一旦陷入死锁,lock()就陷入永久等待

1.3.2、tryLock()

  • tryLock()用来尝试获取锁,如果当前锁没有被其他线程占用,则获取成功,返回true,否则返回false,代表获取锁失败。
  • 相比lock,这样的方法显然功能更强大了,我们可以根据是否获取到锁来决定后续程序的行为
  • 该方法会立即返回,即使再拿不到锁时也不会一直在那等

1.3.2、tryLock(long time,TimeUnit unit)

  • 超时就放弃
/**
 * 描述:用tryLock来避免死锁
 */
public class TryLockDeadLock implements Runnable {
    int flag = 1;
    static Lock lock1 = new ReentrantLock();
    static Lock lock2 = new ReentrantLock();


    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (flag == 1) {
                try {
                    if (lock1.tryLock(800, TimeUnit.MILLISECONDS)) {
                        try {
                            System.out.println("线程1获取到了锁1");
                            Thread.sleep(new Random().nextInt(1000));
                            if (lock2.tryLock(800, TimeUnit.MILLISECONDS)) {
                                try {
                                    System.out.println("线程1获取到了锁2");
                                    System.out.println("线程1成功获取到了两把锁");
                                    break;
                                } finally {
                                    lock2.unlock();
                                }
                            } else {
                                System.out.println("线程1获取锁2失败,已重试");
                            }
                        } finally {
                            lock1.unlock();
                            Thread.sleep(new Random().nextInt(1000));
                        }
                    } else {
                        System.out.println("线程1获取锁1失败,已重试");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            if (flag == 0) {
                try {
                    if (lock2.tryLock(3000, TimeUnit.MILLISECONDS)) {
                        try {
                            System.out.println("线程2获取到了锁2");
                            Thread.sleep(new Random().nextInt(1000));
                            if (lock1.tryLock(800, TimeUnit.MILLISECONDS)) {
                                try {
                                    System.out.println("线程2获取到了锁1");
                                    System.out.println("线程2成功获取到了两把锁");
                                    break;
                                } finally {
                                    lock1.unlock();
                                }
                            } else {
                                System.out.println("线程2获取锁1失败,已重试");
                            }
                        } finally {
                            lock2.unlock();
                            Thread.sleep(new Random().nextInt(1000));
                        }
                    } else {
                        System.out.println("线程2获取锁2失败,已重试");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        TryLockDeadLock tryLockDeadLock1 = new TryLockDeadLock();
        TryLockDeadLock tryLockDeadLock2 = new TryLockDeadLock();
        tryLockDeadLock1.flag = 1;
        tryLockDeadLock2.flag = 0;
        new Thread(tryLockDeadLock1).start();
        new Thread(tryLockDeadLock2).start();
    }
}

1.3.3、lockInterruptibly()

  • 相当于tryLock(long time,TimeUnit unit)把超时时间设置为无限。在等待锁的过程中,线程可以被中断

1.3.4 unlock

  • 解锁
  • 应该被写在finally里面,然后再去写业务逻辑。不写就会导致死锁

课程收获

今天学习了锁的相关知识:
图片描述

点击查看更多内容
1人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
6
获赞与收藏
11

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消