网站首页 > java教程 正文
介绍CAS操作前,我们先简单看一下乐观锁 与 悲观锁这两个常见的锁概念。
悲观锁:
从Java多线程角度,存在着“可见性、原子性、有序性”三个问题,悲观锁就是假设在实际情况中存在着多线程对同一共享的竞争,所以在操作前先占有共享资源(悲观态度)。因此,悲观锁是阻塞,独占的,存在着频繁的线程上下文切换,对资源消耗较大。synchronized就是悲观锁的一种实现。
乐观锁:
如名一样,每次操作都认为不会发生冲突,尝试执行,并检测结果是否正确。如果正确则执行成功,否则说明发生了冲突,回退再重新尝试。乐观锁的过程可以分为两步:冲突检测 和 数据更新。在Java多线程中乐观锁一个常见实现即:CAS操作。
CAS
CAS,(Compare-And-Swap,比较和替换)。其具有三个操作数:内存地址V,旧的预期值A,新的预期值B。当V中的值和A相同时,则用新值B替换V中的值,否则不执行更新。(PS:上述的操作是原子性的,因为过程是:要么执行更新,要么不更新)
新建一个接口,统一加锁入口:
public interface MyCAS {
void doSomething();
}
加锁工具类:
import java.util.concurrent.atomic.AtomicReference;
public class MyCASUtil {
//封装为原子类
private static final AtomicReference<Thread> atomicThread = new AtomicReference<Thread>();
//执行类
public static void doSomethingCAS(MyCAS myCAS) {
lock();
myCAS.doSomething();
unLock();
}
//前置方法 加锁
private static void lock() {
//获取当前线程
Thread thread = Thread.currentThread();
while (!atomicThread.compareAndSet(null, thread)){
//稍微一等 节省cpu开销
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//后置方法 解锁
private static void unLock() {
Thread thread = Thread.currentThread();
atomicThread.compareAndSet(thread, null);
}
}
其中的compareAndSet()方法是一个原子操作, 当current值符合expect时,用next替换了current。如果不符合,则在循环中不断尝试,直到成功为止。
在这个例子中我们要怎么使用呢? 我们可以新建一个类实现MyCAS这个接口,把需要加锁的代码放在方法doSomething中,使用方法举例:
public class MyCASTest implements MyCAS {
private int val;
MyCASTest(int val) {
this.val = val;
}
public void doSomething() {
System.out.println(val);
try {
Thread.sleep(100);
val++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试:
public class MainTest {
public static void main(String[] args) {
final MyCAS casTest = new MyCASTest(0);
ExecutorService executor = newFixedThreadPool(10);
for (int i = 0; i < 100;i++) {
executor.execute(new Runnable() {
public void run() {
//加锁的情况
MyCASUtil.doSomethingCAS(casTest);
//不加锁的情况
//casTest.doSomething();
}
});
}
executor.shutdown();
}
}
CAS存在的问题
1. ABA问题
ABA问题值,内存地址V的值是A,线程one从V中取出了A,此时线程two也从V中取出了A,同时将A修改为B,但是又因为一些原因修改为A。而此时线程one仍看到V中的值为A,认为没有发生变化,此为ABA问题。解决ABA问题一种方式是通过版本号(version)。每次执行数据修改时,都需要带上版本号,如:1A,2B,3A。通过比较版本号可知是否有发生过操作,也就解决了ABA问题。在我们这个例子中并不存在ABA问题。
2. 未知的等待时长
因为CAS采取失败重试的策略,所以不确定会发生多少次循环重试。如果在竞争激烈的环境下,其重试次数可能大幅增加。此时效率也就降低了。
猜你喜欢
- 2025-06-23 Java阻塞队列:LinkedTransferQueue
- 2025-06-23 Java面试题-锁的膨胀升级过程(锁的机制升降级)
- 2025-06-23 Java中CurrentHashMap的使用原理?
- 2025-06-23 Java并发锁机制和Atomic原理解析(java并发的时候常用的处理方式)
- 2025-06-23 「Java多线程」内置锁(Synchronized)的前世今生
- 2025-06-23 Java 面试题:乐观锁和悲观锁的理解及如何实现,有哪些实现方式?
- 2025-06-23 万字详文:腾讯高可用、高性能 ZooKeeper 源码和实践揭秘
- 2025-06-23 ReentrantLock源码解析:ReentrantLock 的实现原理与 AQS 机制
- 2025-06-23 Java面试题整理:紧跟2025年面试趋势
- 2025-06-23 面试官:说一下 synchronized 锁机制原理 与 Lock 锁机制
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)