Java总结(2)–并发编程
本文最后更新于512 天前,其中的信息可能已经过时,如有错误请发送邮件到mapleleaf2333@gmail.com

悲观锁

总是假设最坏情况,即共享资源每次被访问的时候就会出现问题。即共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程

独占锁:synchronized、ReentrantLock

缺点:

  • 高并发情况易造成线程阻塞
  • 大量阻塞带来频繁的上下文切换,增加系统性能开销
  • 易造成死锁

常用场景

用于写比较多的情况,可以避免频繁失败和重试影响性能,它的开销是固定的。

乐观锁

总是假设最好的情况,认为共享资源每次被访问的时候不会出现问题,线程可以不停地执行,无需加锁也无需等待。只是在提交修改的时候去验证对应的资源是否被其它线程修改了。

优点:

不存在锁竞争造成线程阻塞,也不会有死锁的问题,在性能上往往会更胜一筹

缺点

如果冲突频繁发生(写占比非常多的情况),会频繁失败和重试,很影响性能。

原子变量类:AtomicInteger、LongAdder

常见问题

  • ABA问题:某段时间变量的值可能被改为其他值,然后又改回 A。单纯CAS会误认为其没修改。解决方法是添加版本号时间戳
  • 循环时间长开销大:CAS用到自旋操作来进行重试,也就是不成功就一直循环执行直到成功,容易造成巨大开销。使用pause指令可以缓解:1.延迟流水线执行;2.避免退出循环时因内存顺序冲突导致CPU流水线被清空。
  • 只能保证一个共享变量的原子操作:CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 无效。解决方法:用AtomicReference(把多个变量放在一个对象里来进行 CAS 操作),相当于把多个共享变量合并成一个共享变量来操作。

常用场景

通常多于写比较少的情况下(多读场景,竞争较少),这样可以避免频繁加锁影响性能。

乐观锁实现

版本号机制

数据表中加上一个数据版本号 version 字段,表示数据被修改的次数,修改时version+1。当线程 A 要更新数据值时,在读取数据的同时也会读取 version 值,在提交更新时,若刚才读取到的 version 值为当前数据库中的 version 值相等时才更新,否则重试更新操作,直到更新成功。

CAS算法

用一个预期值和要更新的变量值进行比较,两值相等才会进行更新

CAS 是一个原子操作,底层依赖于一条 CPU 的原子指令。

CAS 涉及到三个操作数:

  • V:要更新的变量值(Var)
  • E:预期值(Expected)
  • N:拟写入的新值(New)

当且仅当 V 的值等于 E 时,CAS 通过原子方式用新值 N 来更新 V 的值。如果不等,说明已经有其它线程更新了 V,则当前线程放弃更新。

文章作者: 落尘Alko
链接: http://mapleleaf666.vip/?p=498
来源: 落尘Alko的小窝
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇