java并发编程

线程锁

锁是并发编程中的一个重要概念,用于控制对共享资源的访问,以避免数据不一致和竞争条件;

synchronized

java提供的关键字,用来标记方法、代码块、类。 特点: 使用简单、悲观锁、颗粒度大、可重入

Lock
  • ReentranLock 特点: 可选择公平锁(先到先得)、灵活、可重入、悲观锁; 提供了Condition可用于线程间相互等待和唤醒 tryLock提供了阻塞和非阻塞的方式

  • ReentrantReadWriteLock

    • ReadLock : 共享锁,允许多个线程同时持有读锁,写锁被持有时,不可读

    • WriteLock : 排他锁,只有一个线程可以持有写锁,且会阻塞其他读锁线程;相反如果有读锁被持有时,获取写锁需要等待所有读锁释放

分布式锁

sync关键字和locks包都只适用于单机应用,如果是集群或分布式则需要借助一些中间件,常用的有redis和zookeeper

redis:

可以直接使用Redisson,只需要导入依赖并配置就可以直接使用。实现上主要有两点

  • 使用lua脚本来保证加锁/解锁操作的原子性, 其中加锁涉及两个命令

    hincrby和pexpire,使用lua脚本来执行避免hincrby成功而pexpire失败下导致死锁的问题
    编辑编辑编辑
    这里使用hash,用线程id作为hash的key,value为持有锁的次数;对应的unLock时,会对value进行-1操作,以此来实现可重入锁;需要注意有一次加锁就要有一次解锁
  • 锁的续期,通过守护线程定时的延迟锁的过期时间,避免应用处理还未完成,锁就已经超时释放的场景;使用守护线程可以避免程序因为异常原因导致锁无法释放

zookeeper:

zk适合做为分布式锁,是使用其中的临时节点。临时节点的特性

  • 自动删除:临时节点在创建它的客户端会话结束时会自动删除。这意味着当客户端崩溃或失去连接时,临时节点会被 ZooKeeper 自动清理。

  • 不可有子节点:临时节点不能创建子节点,这是为了确保节点的树结构简单和清晰。

  • 短生命周期:因为临时节点的生命周期与客户端会话相关联,所以它们通常被用来存储短期数据。

乐观锁

乐观锁的核心思想是“先做再检查”,例如 update table set i=1 where id =1 and i=2; 类似这样的思想 满足i=2时才提交更新;

适用场景:

  • 读多写少: 在这种场景下,数据冲突的概率较低,乐观锁可以减少锁定时间

  • 不需要严格一致性的数据: 对于某些数据,可以接受一定程度的重试和更新失败,乐观锁的开销和复杂度较低。

优点:

  • 减少锁争用

  • 提高并发性能

缺点:

  • 适用场景有限: 在写多读少的场景下,冲突概率高,重试次数多,反而降低性能。

  • 实现复杂: 需要在应用程序中处理冲突和重试逻辑,增加了代码复杂度。

CAS

CAS(Compare-And-Swap)是一种无锁(lock-free)的原子操作,用于在多线程环境中进行并发控制。它用于实现无锁数据结构和算法,以提高并发性能并避免传统锁的开销。CAS 的核心思想是通过比较和交换操作来确保数据的一致性。

CAS 操作包括三个主要步骤:

  1. 比较(Compare):检查内存位置的当前值是否与预期值相等。

  2. 交换(Swap):如果当前值与预期值相等,则将该内存位置的值更新为新值。

  3. 返回:返回操作的结果,指示操作是否成功(即是否执行了交换)。

例子:

AtomicInteger/ConcurrentLinkedQueue: 使用Unsafe提供的原子操作,至于底层有人说是通过C++库调用CPU指令集实现的;

volatile关键字

它用于保证多线程环境下的变量可见性和禁止指令重排序;

变量可见性:

正常线程修改一个变量时,修改结果会在线程本地缓存中立即生效,但在主内存中看到的可能还是旧值;

valatile则是保证该变量的值在被修改时,会立即被刷新到主内存中

指令重排

https://zhuanlan.zhihu.com/p/410816240

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值