目的
方便后续自己复习方便, 也是一次学习的记录.
资源
悲观锁
synchronized 和 LOCK 锁
① 其核心思想是: 线程只有占有了锁, 才能去操作共享变量, 每次只有一个线程占锁成功, 获取锁失败的线程, 都得停下来等待.
② 线程从运行到阻塞、再从阻塞到唤醒, 涉及线程上下文切换, 如果频繁发生, 影响性能.
③ 实际上, 线程在获取synchronized和Lock锁时, 如果锁已被占用, 都会做几次重试操作, 减少阻塞的机会
乐观锁
AtomicInteger 使用 CAS 来保证原子性
① 其核心思想是: 无需加锁, 每次只有一个线程能成功修改共享变量, 其他失败的线程不需要停止, 不断重试直至成功.
② 由于线程一直运行, 不需要阻塞, 因此不涉及线程上下文切换.
③ 它需要多核CPU支持, 且线程数不超过CPU核数
代码演示
/**
* 悲观锁和乐观锁区别
* <p>
* Setting -> Java Compiler -> Override compiler parameters pre-module
* --add-exports=java.base/jdk.internal.access=ALL-UNNAMED
* --add-exports=java.base/jdk.internal=ALL-UNNAMED
* --add-exports=java.base/jdk.internal.misc=ALL-UNNAMED
*
* @author xiaozhengN [email protected]
* @since 2022-11-20 16:55:07
**/
@Slf4j
public class SyncVsCas {
static final Unsafe UNSAFE = Unsafe.getUnsafe();
static final long BALANCE = UNSAFE.objectFieldOffset(Account.class, "balance");
/**
* 账户对象
*/
@Data
@Builder
static class Account {
// 账户余额
volatile int balance;
}
// 悲观锁例子
private static void sync(Account account) {
new Thread(() -> {
synchronized (account) {
int oldValue = account.balance;
int newValue = oldValue + 5;
account.balance = newValue;
}
}).start();
new Thread(() -> {
synchronized (account) {
int oldValue = account.balance;
int newValue = oldValue - 5;
account.balance = newValue;
}
}).start();
log.info("账户余额: " + account.balance);
}
// 乐观锁例子
private static void cas(Account account) {
new Thread(() -> {
while (true) {
int oldValue = account.balance;
int newValue = oldValue + 5;
// 比较交换
if (UNSAFE.compareAndSetInt(account, BALANCE, oldValue, newValue)) {
break;
}
}
}).start();
new Thread(() -> {
while (true) {
int oldValue = account.balance;
int newValue = oldValue - 5;
// 比较交换
if (UNSAFE.compareAndSetInt(account, BALANCE, oldValue, newValue)) {
break;
}
}
}).start();
log.info("账户余额: " + account.balance);
}
public static void main(String[] args) {
sync(Account.builder().balance(10).build());
cas(Account.builder().balance(10).build());
}
}