JAVA模拟CAS
2014-04-06 21:43
176 查看
先来看一个线程不安全的Counter:
下面来看使用JAVA5以及其后版本提供的原子变量实现的Counter:
大多数现代处理器都包含对多处理的支持。当然这种支持包括多处理器可以共享外部设备和主内存,同时它通常还包括对指令系统的增加来支持多处理的特殊要求。特别是,几乎每个现代处理器都有通过可以检测或阻止其他处理器的并发访问的方式来更新共享变量的指令。
现在的处理器(包括 Intel 和 Sparc 处理器)使用的最通用的方法是实现名为“比较并交换(Compare And Swap)”或 CAS 的原语。(在 Intel 处理器中,比较并交换通过cmpxchg 系列指令实现。PowerPC 处理器有一对名为“加载并保留”和“条件存储”的指令,它们实现相同的目地;MIPS 与 PowerPC 处理器相似,除了第一个指令称为“加载链接”。)
CAS 操作包含三个操作数—— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前值。)CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”
通常将 CAS 用于同步的方式是从地址 V 读取值 A,执行多步计算来获得新值 B,然后使用 CAS 将 V 的值从 A 改为 B。如果 V 处的值尚未同时更改,则 CAS 操作成功。
类似于 CAS 的指令允许算法执行读-修改-写操作,而无需害怕其他线程同时修改变量,因为如果其他线程修改变量,那么 CAS 会检测它(并失败),算法可以对该操作重新计算。
现在我们模拟CAS来实现一个计数器:
CAS 失败,调用者可以重试 CAS 操作或采取其他适合的操作。
package org.wangld.test.concurrence.cas; public class UnsafeCounter implements Runnable { private int counter = 0; @Override public void run() { for (int i = 0; i < 10000; i++) { System.out.println(counter++); } } public static void main(String[] args) { Runnable run = new UnsafeCounter(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); } }因为counter++是一个非原子性操作,可以分成读counter,加1,写counter这3步,这就导致了线程安全问题。这个例子中,每个线程要让counter加10000次1,10个线程就是100000,但是最终的执行结果基本都不到100000.
下面来看使用JAVA5以及其后版本提供的原子变量实现的Counter:
package org.wangld.test.concurrence.cas; import java.util.concurrent.atomic.AtomicInteger; public class AtomicCounter implements Runnable { private AtomicInteger value = new AtomicInteger(); @Override public void run() { for (int i = 0; i < 10000; i++) { System.out.println(value.incrementAndGet()); } } //安全 public static void main(String[] args) { Runnable run = new AtomicCounter(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); } }上面这个计数器没有使用锁,但在面对并发的时候就不存在线程安全问题。因为AtomicInteger采用了系统的CAS。下面这段话讲解了CAS,摘自《JAVA并发编程实践》:
大多数现代处理器都包含对多处理的支持。当然这种支持包括多处理器可以共享外部设备和主内存,同时它通常还包括对指令系统的增加来支持多处理的特殊要求。特别是,几乎每个现代处理器都有通过可以检测或阻止其他处理器的并发访问的方式来更新共享变量的指令。
现在的处理器(包括 Intel 和 Sparc 处理器)使用的最通用的方法是实现名为“比较并交换(Compare And Swap)”或 CAS 的原语。(在 Intel 处理器中,比较并交换通过cmpxchg 系列指令实现。PowerPC 处理器有一对名为“加载并保留”和“条件存储”的指令,它们实现相同的目地;MIPS 与 PowerPC 处理器相似,除了第一个指令称为“加载链接”。)
CAS 操作包含三个操作数—— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前值。)CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”
通常将 CAS 用于同步的方式是从地址 V 读取值 A,执行多步计算来获得新值 B,然后使用 CAS 将 V 的值从 A 改为 B。如果 V 处的值尚未同时更改,则 CAS 操作成功。
类似于 CAS 的指令允许算法执行读-修改-写操作,而无需害怕其他线程同时修改变量,因为如果其他线程修改变量,那么 CAS 会检测它(并失败),算法可以对该操作重新计算。
现在我们模拟CAS来实现一个计数器:
package org.wangld.test.concurrence.cas; public class CASCounter implements Runnable { private SimilatedCAS counter = new SimilatedCAS(); @Override public void run() { for (int i = 0; i < 10000; i++) { System.out.println(this.increment()); } } public int increment() { int oldValue = counter.getValue(); int newValue = oldValue + 1; while (!counter.compareAndSwap(oldValue, newValue)) {//如果CAS失败,就去拿新值继续执行CAS oldValue = counter.getValue(); newValue = oldValue + 1; } return newValue; } public static void main(String[] args) { Runnable run = new CASCounter(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); } } class SimilatedCAS { private int value; public int getValue() { return value; } // 这里只能用synchronized了,毕竟无法调用操作系统的CAS public synchronized boolean compareAndSwap(int expectedValue, int newValue) { if (value == expectedValue) { value = newValue; return true; } return false; } }如果每个线程在其他线程任意延迟(或甚至失败)时都将持续进行操作,就可以说该算法是“无等待”的。“无锁定算法”要求某个线程总是执行操作(无等待的另一种定义是保证每个线程在其有限的步骤中正确计算自己的操作,而不管其他线程的操作、计时、交叉或速度)。基于 CAS 的并发算法称为“无锁定算法”,因为线程不必再等待锁定(有时称为互斥或关键部分,这取决于线程平台的术语)。无论 CAS 操作成功还是失败,在任何一种情况中,它都在可预知的时间内完成。如果
CAS 失败,调用者可以重试 CAS 操作或采取其他适合的操作。
相关文章推荐
- Java模拟CAS的实现
- J.U.C--CAS用Java模拟实现
- java模拟死锁事故现场
- 关于java模拟ie 访问web网站的解决方法
- JAVA模拟发送HTTP请求
- JAVA模拟HTTP post请求上传图片
- JAVA CAS原理深度分析
- 用java代码模拟鼠标双击事件
- Java_模拟comet的实现
- Java反射及依赖注入简单模拟
- java多线程模拟抢红包功能
- java模拟电子表,打印火柴类型时间
- java 模拟表单方式提交上传文件
- java并发编程学习: 原子变量(CAS)
- java后端模拟表单提交
- JAVA线程并发性之CAS算法,模拟实现代码
- [记着玩]java模拟链表逆序输出结果
- Java 模拟form表单提交
- java模拟post方式提交表单实现图片上传
- Java基础之IO流,利用StringBuilder模拟BufferedReader的readLine方法