java中的锁

java中的锁

  1. 乐观锁和悲观锁
    乐观锁和悲观锁是一种java并发思想;
    • 悲观锁:悲观锁认为任何操作都会修改数据,所以都会加锁;
      适用场景:数据库实现方式,使用数据库的读锁、写锁、行锁等实现进程的悬挂阻塞等当前操作完成后才能进行下一个操作。二、在Java里面可以使用synchronize实现悲观锁。
    • 乐观锁:乐观锁认为任何操作都不会修改数据,操作数据之前要判断数据是否与之前的数据一致,一致则进行操作;(适用DB的读大于写的业务场景)
      适用场景:目前比较常用的有两种方式,第一种是使用版本号或者时间戳。在表中加个version或updatetime字段,在每次更新操作时对此一下该字段,如果一致则更新数据,数据不等则放弃本次修改,根据实际业务需求做相应的处理。第二种是CAS方式,即Java中的compareAndSwap。CAS操作涉及到三个操作数,内存值(valueOffSet)、期望值(expect)、更新值(update)。当内存值与期望值一致时就会更新数据,反之不操作。
  2. 公平锁与非公平锁
    根据线程获取锁的抢占机制,锁又可以分为公平锁和非公平锁。
    • 公平锁
      公平锁是指多个线程按照申请锁的顺序来获取锁。
      优点:所以线程都可以拿到锁,不会导致饿死在队列;
      缺点:每次cpu都要去唤醒锁,需耗费资源,且每次只能唤醒队列中的第一个线程,吞吐量减少;
    • 非公平锁
      非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。ReentrantLock 提供了公平锁和非公平锁的实现。
      优点:减少cpu唤醒队列中线程的次数,吞吐量高;
      缺点:有的线程可能会被饿死在队列,始终无法拿到锁;
  3. 独占锁与共享锁
    根据锁能否被多个线程持有,可以把锁分为独占锁和共享锁。
    • 独占锁
      独占锁是指任何时候都只有一个线程能执行资源操作。
    • 共享锁
      共享锁指定是可以同时被多个线程读取,但只能被一个线程修改。比如 Java 中的 ReentrantReadWriteLock 就是共享锁的实现方式,它允许一个线程进行写操作,允许多个线程读操作。
  4. 可重入锁
    可重入锁指的是该线程获取了该锁之后,可以无限次的进入该锁锁住的代码。
    应用场景:
    • 用在定时任务时,如果任务执行时间可能超过下次计划执行时间,确保该有状态任务只有一个正在执行,忽略重复触发。
    • 用在界面交互时点击执行较长时间请求操作时,防止多次点击导致后台重复执行(忽略重复触发)。
  5. 自旋锁
    自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗 CPU。
  6. CAS 的 ABA问题
    CAS(Compare and Swap)比较并交换,是一种乐观锁的实现,是用非阻塞算法来代替锁定,其中 java.util.concurrent 包下的 AtomicInteger 就是借助 CAS 来实现的。
    但 CAS 也不是没有任何副作用,比如著名的 ABA 问题就是 CAS 引起的。
    老王去银行取钱,余额有 200 元,老王取 100 元,但因为程序的问题,启动了两个线程,线程一和线程二进行比对扣款,线程一获取原本有 200 元,扣除 100 元,余额等于 100 元,此时阿里给老王转账 100 元,于是启动了线程三抢先在线程二之前执行了转账操作,把 100 元又变成了 200 元,而此时线程二对比自己事先拿到的 200 元和此时经过改动的 200 元值一样,就进行了减法操作,把余额又变成了 100 元。这显然不是我们要的正确结果,我们想要的结果是余额减少了 100 元,又增加了 100 元,余额还是 200 元,而此时余额变成了 100 元,显然有悖常理,这就是著名的 ABA 的问题。
    执行流程如下。
  • 线程一:取款,获取原值 200 元,与 200 元比对成功,减去 100 元,修改结果为 100 元。
  • 线程二:取款,获取原值 200 元,阻塞等待修改。
  • 线程三:转账,获取原值 100 元,与 100 元比对成功,加上 100 元,修改结果为 200 元。
  • 线程二:取款,恢复执行,原值为 200 元,与 200 元对比成功,减去 100 元,修改结果为 100 元。
    最终的结果是 100 元。
    ABA 问题的解决
    常见解决 ABA 问题的方案加版本号,来区分值是否有变动。以老王取钱的例子为例,如果加上版本号,执行流程如下。
  • 线程一:取款,获取原值 200_V1,与 200_V1 比对成功,减去 100 元,修改结果为 100_V2。
  • 线程二:取款,获取原值 200_V1 阻塞等待修改。
  • 线程三:转账,获取原值 100_V2,与 100_V2 对比成功,加 100 元,修改结果为 200_V3。
  • 线程二:取款,恢复执行,原值 200_V1 与现值 200_V3 对比不相等,退出修改。
    最终的结果为 200 元,这显然是我们需要的结果。
    在程序中,要怎么解决 ABA 的问题呢?
    在 JDK 1.5 的时候,Java 提供了一个 AtomicStampedReference 原子引用变量,通过添加版本号来解决 ABA 的问题,具体使用示例如下:
String name = "老王";
String newName = "Java";
AtomicStampedReference<String> as = new AtomicStampedReference<String>(name, 1);
System.out.println("值:" + as.getReference() + " | Stamp:" + as.getStamp());
as.compareAndSet(name, newName, as.getStamp(), as.getStamp() + 1);
System.out.println("值:" + as.getReference() + " | Stamp:" + as.getStamp());

以上程序执行结果如下:

值:老王 | Stamp1
值:Java | Stamp2
  1. synchronized 是哪种锁的实现?为什么?
    synchronized 是悲观锁的实现,因为 synchronized 修饰的代码,每次执行时会进行加锁操作,同时只允许一个线程进行操作,所以它是悲观锁的实现。
  2. new ReentrantLock() 创建的是公平锁还是非公平锁?
    非公平锁
  3. synchronized 使用的是公平锁还是非公平锁?
    synchronized 使用的是非公平锁,并且是不可设置的。这是因为非公平锁的吞吐量大于公平锁,并且是主流操作系统线程调度的基本选择,所以这也是 synchronized 使用非公平锁原由。
  4. 为什么非公平锁吞吐量大于公平锁?
    答:比如 A 占用锁的时候,B 请求获取锁,发现被 A 占用之后,堵塞等待被唤醒,这个时候 C 同时来获取 A 占用的锁,如果是公平锁 C 后来者发现不可用之后一定排在 B 之后等待被唤醒,而非公平锁则可以让 C 先用,在 B 被唤醒之前 C 已经使用完成,从而节省了 C 等待和唤醒之间的性能消耗,这就是非公平锁比公平锁吞吐量大的原因。
  5. volatile 的作用是什么?
  • 保证了不同线程对共享变量进行操作时的可见性,即一个线程修改了共享变量的值,共享 变量修改后的值对其他线程立即可见
  • 通过禁止编译器、CPU 指令重排序和部分 happens-before 规则,解决有序性问题
  1. volatile 对比 synchronized 有什么区别?
    synchronized 既能保证可见性,又能保证原子性,而 volatile 只能保证可见性,无法保证原子性。比如,i++ 如果使用 synchronized 修饰是线程安全的,而 volatile 会有线程安全的问题。
  2. CAS 是如何实现的?
    CAS(Compare and Swap)比较并交换,CAS 是通过调用 JNI(Java Native Interface)的代码实现的,比如,在 Windows 系统 CAS 就是借助 C 语言来调用 CPU 底层指令实现的。
  3. CAS 会产生什么问题?应该怎么解决?
    CAS 是标准的乐观锁的实现,会产生 ABA 的问题(详见正文)。ABA 通常的解决办法是添加版本号,每次修改操作时版本号加一,这样数据对比的时候就不会出现 ABA 的问题了。
    参考自:https://blog.csdn.net/GitChat/article/details/104871532
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值