美文网首页编程语言爱好者Java
Java程序员:如何根据不同的业务场景,来选择合适的锁?

Java程序员:如何根据不同的业务场景,来选择合适的锁?

作者: 一只程序猿哟 | 来源:发表于2022-04-21 16:04 被阅读0次

刚开始我看到这个标题的时候我感觉“很熟悉,但是又很陌生”,因为锁是有效的解决并发情况下保证临界资源操作原子性的有效手段之一。下面我就从我们几个开发使用的角度来说我们常用的锁。

锁可以解决什么问题?

锁可以解决并行执行任务执行过程中对,共享数据顺序访问、修改的场景。比如对同一个账户进行并行扣款或者转账。下面我们展开讨论下 synchronized 、ReetranLock 以及他们的使用。

synchronized

synchronized 是 JDK 提供的内置锁, 由 JVM 虚拟机内部实现,是基于 monitor 机制, 在 JDK 1.6 之后被优化,会有一个锁升级的过程,将锁的状态存储到对象头中。

锁升级过程,默认是无锁状态,首先会进行判断,如果是没有字段竞争的情况下会使用偏向锁,偏向锁的本质就是将当前获得锁的线程 id 设置到共享数据的对象头中。

然后升级为轻量级锁,轻量级锁的本质是通过 CAS 来修改 MarkWord 来实现的。

最后再升级为重量级锁,我们可以通过操作系统的 monitor 依赖操作系统的 MutexLock(互斥锁)来实现的 。

四种使用方式

  1. 在静态方法上使用
  2. 在普通方法上使用
  3. 锁定 this 状态
  4. 锁定静态类

加锁状态记录位置

对象加锁,记录在对象头中,对象头入下图所示。

[图片上传失败...(image-11ecdd-1650528230582)]

在运行期间,Mark Word里面存储的数据会随着锁标志位的变化而变化。Mark Word可能变为存储以下4种数据,如下图所示

[图片上传失败...(image-149d9e-1650528230582)]

锁的膨胀和升级

锁的升级和膨胀时候不可逆转的。

[图片上传失败...(image-c47442-1650528230582)]

使用场景

JDK 在并发包中, 使用 synchroinzed 的地方有:

  1. ConcurrentHashMap (jdk 1.8)
  2. HashTable

ReetrantLock

ReetrantLock 开发作者是 Doug Lea ,从 JDK1.5 开始过后加入 JDK 的锁,主要是通过 QAS 的方式来实现的, 通过 Unsafe 包提供的 CAS 操作来进行锁状态(state)的竞争。然后通过 LockSupport.park(this). 进行 park 住线程,如果在 AQS 队列头的对象进行唤醒执行 unpack 方法,然后让他去竞争锁。

ReetrantLock 还分为公平锁和非公平锁,默认是非公平锁。 因为公平锁,是需要保证竞争者按照获取锁的顺序进行获得,性能略低于非公平锁。

AQS 队列结构如下所示,它的本质是一个 FIFO 的线程安全的同步队列,如下图所示:

[图片上传失败...(image-dabae4-1650528230581)]

ReetrantLock 加锁和解锁的过程入下图所示:

[图片上传失败...(image-785470-1650528230581)]

使用方式

ReetrantLock 的使用方式如下,主要是有三个步骤:创建、加锁、解锁。

 class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m() {
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }

使用场景

JDK 在并发包中, 使用 ReetrantLock 的地方有:

  1. CyclicBarrier
  2. DelayQueue
  3. LinkedBlockingDeque
  4. ThreadPoolExecutor
  5. ReentrantReadWriteLock
  6. StampedLock

上面我只是列举了一步分,对于 ReetrantLock 来看可以说是并发包中非常基础的类,也是我们学习并发的基础,在后续的文章中我会给展开做更加深入的分析。

如何选择锁?

  1. 对于单机环境我们在 JDK 内进行并发控制我们可以使用 synchronized (内置锁) 和 RentrantLock 。
  2. 对于自增或者原子数据累计我们可以使用 Unsafe 提供的原子类,比如 AtomicInteger , AtomicLong
  3. 对于数据库的话,对于用户金额扣除的场景我们可以使用乐观锁的方式来进行控制,SQL 如下
update table_name set amount = 100, 
                      version = version + 1 where id = 1 and version = 1;
  1. 对于分布式场景下我们需要保证一致性,可以使用 Redis 或者 Zk 实现分布式锁。来进行分布式场景下的并发控制。

相关文章

  • Java程序员:如何根据不同的业务场景,来选择合适的锁?

    刚开始我看到这个标题的时候我感觉“很熟悉,但是又很陌生”,因为锁是有效的解决并发情况下保证临界资源操作原子性的有效...

  • Java图形化编程之布局管理器

    在Java图形化编程中,我们根据不同的业务场景选择不同的布局容器来满足我们的需求。那么Java为我们提供了多少布局...

  • 乐观锁与悲观锁

    无论是悲观锁还是乐观锁,都是并发控制的一种思想。并不仅局限于数据库,具体如何选择乐观锁和悲观锁是根据业务场景来的。...

  • 大数据面试题以及答案整理(二)

    ​Redis性能优化,单机增加CPU核数是否会提高性能 1、根据业务需要选择合适的数据类型,并为不同的应用场景设置...

  • 对于缺失值的处理

    建议:不同场景下的数据缺失机制不同,这需要工程师基于对业务选择合适的填充方法。 如何判断缺失值类型?缺失值的分类按...

  • 文章总结(7)—微服务

    1. 微服务 微服务和单体架构适合的场景? 在不同的场景下,选择合适的架构进行开发: 单体应用适合场景: 业务场景...

  • Essential Scala: Import Specfica

    Scala是一门很自由的程序设计语言,程序员可以根据具体场景,选择最合适的武器来解决所面对的问题。例如,相对于Ja...

  • 根据不同环境需求来选择合适的光模块?

    随着信息化的高速发展,光通信应用已经越来越普及,大到远距离的高速骨干网、小到光纤宽带入户,光纤凭借大容量高速传输优...

  • 深入理解锁机制(Java)

    Java提供了丰富种类的锁,在适当的场景使用合适的锁能够展现出非常高的效率。常用到的锁就有乐观锁和悲观锁。 乐观锁...

  • 高并发场景下强一致预算/库存扣减方案

    场景描述 对于预算扣减/库存扣减类场景,我们需要根据业务对已有预算/库存做减法,拿发券的场景来举例: 需要满足不同...

网友评论

    本文标题:Java程序员:如何根据不同的业务场景,来选择合适的锁?

    本文链接:https://www.haomeiwen.com/subject/tfaiertx.html