栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

锁策略(多线程)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

锁策略(多线程)

1.乐观锁 vs 悲观锁 悲观锁: 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。 乐观锁: 假设数据一般情况下不会产生并发冲突,所以在数据进行提交更新的时候,才会正式对数据是否产生并发冲突进行检测,如果发现并发冲突了,则让返回用户错误的信息,让用户决定如何去做。 以乐观的心态看待线程冲突(总是觉得没有线程会同时操作共享变量),所以每次都不加锁(程序层面),就直接操作共享变量。 2.读写锁 一个线程对于数据的访问 , 主要存在两种操作 : 读数据和写数据 .
  • 两个线程都只是读一个数据, 此时并没有线程安全问题. 直接并发的读取即可.
  • 两个线程都要写一个数据, 有线程安全问题.
  • 一个线程读另外一个线程写, 也有线程安全问题.
读锁
        ReadLock readLock = readWriteLock.readLock();
        readLock.lock();//读锁加锁
        readLock.unock();//读锁释放锁
写锁
        WriteLock writeLock = readWriteLock.writeLock();
        writeLock.lock();//写锁加锁
        writeLock.unock();//写锁释放锁
其中 ,
  • 读加锁和读加锁之间, 不互斥.
  • 写加锁和写加锁之间, 互斥.
  • 读加锁和写加锁之间, 互斥.
3.自旋锁(Spin Lock)

一般会搭配乐观锁一起来实现

        do {
            获取变量的值
        }while(!compareAndSwap(变量的值));

尝试修改变量的值:

  • 修改成功,就退出循环,接着往下执行
  • 修改失败,就再次循环
4.公平锁 vs 非公平锁 公平锁 定义:

以申请锁的时间先后顺序,来获取到锁——类似排队买票的方式。

缺点:

效率稍微差一些。

非公平锁 定义:

synchronized申请对象锁,是竞争的方式。

优点:

效率更高(不考虑执行顺序)。

缺点:

可能出现线程饥饿(某些线程长期得不到执行)的现象。

5.CAS 定义:

CAS是乐观锁的一种实现,CAS全称Compare and swap,字面意思:”比较并交换“,CAS的操作主要是由以下几个步骤组成:

  1. 先查询原始值
  2. 操作时比较原始值是否修改
  3. 如果修改,则操作失败,禁止更新操作,如果没有发生修改,则更新为新值。

上述三个步骤是一个原子性操作,不可以被拆分执行。

CAS的实现 针对不同的操作系统, JVM 用到了不同的 CAS 实现原理,简单来讲:
  • java 的 CAS 利用的的是 unsafe 这个类提供的 CAS 操作;
  • unsafe 的 CAS 依赖了的是 jvm 针对不同的操作系统实现的 Atomic::cmpxchg;
  • Atomic::cmpxchg 的实现使用了汇编的 CAS 操作,并使用 cpu 硬件提供的 lock 机制保证其原子性。
简而言之,是因为 硬件予以了支持,软件层面才能做到 。 优势

CAS是一种无锁操作,不需要加锁,避免了线程切换的开销。

缺点

CAS虽然在低并发量的情况下可以减少系统的开销,但是CAS也有一些问题:

  • CPU开销过大问题
  • ABA问题
  • 只能针对一个共享变量
ABA问题

所谓ABA问题,就是比较并交换的循环,存在⼀个时间差,而这个时间差可能带来意想不到的问题。比如线程1和线程2同时也从内存取出A,线程T1将值从A改为B,然后⼜从B改为A。线程T2看到的最终值还是A,经过与预估值的比较,二者相等,可以更新,此时尽管线程T2的CAS操作成功,但不代表就没有问题。

本质:

ABA问题的根本在于cas在修改变量的时候,无法记录变量的状态,比如修改的次数,否修改过这个变量。这样就很容易在⼀个线程将A修改成B时,另一个线程又会把B修改成A,造成cas多次执行的问题。

解决方案:

AtomicStampedReference类来引入版本号,每次操作之后让版本号 +1,执行的时候判断版本号的值,就可以解决ABA问题。

6.synchronized优化 基本特点 结合上面的锁策略 , 我们就可以总结出 , Synchronized 具有以下特性 ( 只考虑 JDK 1.8):
  1. 开始时是乐观锁, 如果锁冲突频繁, 就转换为悲观锁.
  2. 开始是轻量级锁实现, 如果锁被持有的时间较长, 就转换成重量级锁.
  3. 实现轻量级锁的时候大概率用到的自旋锁策略
  4. 是一种不公平锁
  5. 是一种可重入锁
  6. 不是读写锁
加锁工作过程 JVM 将 synchronized 锁分为无锁、偏向锁、轻量级锁、重量级锁状态。会根据情况,进行依次升级。 代价由低到高:
  • 无锁
  • 偏向锁:只有一个线程申请某个对象的锁,就可以使用偏向锁(代价小),同一个线程可重入的方式多次申请同一个对象锁,也是使用偏向锁
  • 轻量级锁:自旋+CAS,线程冲突不严重(没有冲突,或少量冲突):相对重量级锁代价要低
  • 重量级锁:基于系统级别的mutex lock锁,代价就非常大(系统加锁),线程冲突严重的时候使用
锁消除

编译器+JVM 会根据代码运行的情况智能判定当前的锁是否必要,如果不必要,就直接把锁的代码干掉

        //局部变量只有当前方法执行的线程持有(不可能有其他线程持有)
        //也就不存再线程安全问题:jvm给append中synchronized加锁释放锁
        // 优化方案,就是“锁消除”=>不加锁
        StringBuffer sb = new StringBuffer();
        sb.append("a");
        sb.append("b");
        sb.append("c");
锁粗化

 如果一段逻辑中,需要多次解锁解锁,并且解锁时没有其他线程来竞争,此时就会把锁粗化

    private static StringBuffer sb = new StringBuffer();

    public static void main(String[] args) {
        sb.append("a");
        sb.append("b");
        sb.append("c");
        System.out.println(sb.toString());
    }
7.独占锁/共享锁 独占锁

指的是这一把锁只能被一个线程拥有。(synchronized、Lock)

共享锁

指的是一把锁可以被多个线程同时拥有。(ReadWriteLock读写锁,读锁就是共享)

ReadWriteLock读写锁

将锁的粒度更加细化,从而提高锁的性能,可以设置为公平锁。

ReentrantReadWriteLock 读写锁

读锁是共享锁,写锁是独占锁(只有在写的时候才会发生线程不安全问题),读锁和写锁之间是互斥的。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/878490.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号