石家庄网站建设哪家便宜,企业网站 单页,wordpress网页游戏模板,wordpress首页图片导航分类文章目录CAS 与 volatile问题引入代码分析volatile为什么无锁效率高CAS特点原子整数原子引用ABA 问题及解决原子数组原子(字段)更新器原子累加器UnsafeUnsafe CAS 操作管程即 monitor 是阻塞式的悲观锁实现并发控制#xff0c;本文我们将通过非阻塞式的乐观锁的来实现并发控制…
文章目录CAS 与 volatile问题引入代码分析volatile为什么无锁效率高CAS特点原子整数原子引用ABA 问题及解决原子数组原子(字段)更新器原子累加器UnsafeUnsafe CAS 操作管程即 monitor 是阻塞式的悲观锁实现并发控制本文我们将通过非阻塞式的乐观锁的来实现并发控制 CAS 与 volatile
问题引入
有如下需求保证 account.withdraw 取款方法的线程安全
package cn.itcast;
import java.util.ArrayList;
import java.util.List;
interface Account {// 获取余额Integer getBalance();// 取款void withdraw(Integer amount);/*** 方法内会启动 1000 个线程每个线程做 -10 元 的操作* 如果初始余额为 10000 那么正确的结果应当是 0*/static void demo(Account account) {ListThread ts new ArrayList();long start System.nanoTime();for (int i 0; i 1000; i) {ts.add(new Thread(() - {account.withdraw(10);}));}//将线程一个个启动ts.forEach(Thread::start);ts.forEach(t - {try {t.join();} catch (InterruptedException e) {e.printStackTrace();}});long end System.nanoTime();System.out.println(account.getBalance() cost: (end-start)/1000_000 ms);}
}原有实现并不是线程安全的
class AccountUnsafe implements Account {private Integer balance;public AccountUnsafe(Integer balance) {this.balance balance;}Overridepublic Integer getBalance() {return balance;}Overridepublic void withdraw(Integer amount) {balance - amount;}
}解决思路-无锁乐观重试
class AccountSafe implements Account {private AtomicInteger balance;public AccountSafe(Integer balance) {this.balance new AtomicInteger(balance);}Overridepublic Integer getBalance() {return balance.get();}Overridepublic void withdraw(Integer amount) {while (true) {int prev balance.get();int next prev - amount;if (balance.compareAndSet(prev, next)) {break;}}// 可以简化为下面的方法// balance.addAndGet(-1 * amount);}
}代码分析
前面看到的 AtomicInteger 的解决方法内部并没有用锁来保护共享变量的线程安全。那么它是如何实现的呢
public void withdraw(Integer amount) {while(true) {// 需要不断尝试直到成功为止while (true) {// 比如拿到了旧值 1000int prev balance.get();// 在这个基础上 1000-10 990int next prev - amount;/*compareAndSet 正是做这个检查在 set 前先比较 prev 与当前值- 不一致了next 作废返回 false 表示失败比如别的线程已经做了减法当前值已经被减成了 990那么本线程的这次 990 就作废了进入 while 下次循环重试- 一致以 next 设置为新值返回 true 表示成功*/if (balance.compareAndSet(prev, next)) {break;}//或者简洁一点//balance.getAndAdd(-1 * amount);}}
}其中的关键是 compareAndSet它的简称就是 CAS 也有 Compare And Swap 的说法它必须是原子操作。 工作流程
当一个线程要去修改Account对象中的值时先获取值pre调用get方法然后再将其设置为新的值next调用cas方法。在调用cas方法时会将pre与Account中的余额进行比较。 如果两者相等就说明该值还未被其他线程修改此时便可以进行修改操作。如果两者不相等就不设置值重新获取值pre调用get方法然后再将其设置为新的值next调用cas方法直到修改成功为止。
注意
其实 CAS 的底层是 lock cmpxchg 指令X86 架构在单核 CPU 和多核 CPU 下都能够保证【比较-交换】的原子性。在多核状态下某个核执行到带 lock 的指令时CPU 会让总线锁住当这个核把此指令执行完毕再开启总线。这个过程中不会被线程的调度机制所打断保证了多个线程对内存操作的准确性是原子的
volatile
获取共享变量时为了保证该变量的可见性需要使用 volatile 修饰。
它可以用来修饰成员变量和静态成员变量他可以避免线程从自己的工作缓存中查找变量的值必须到主存中获取 它的值线程操作 volatile 变量都是直接操作主存。即一个线程对 volatile 变量的修改对另一个线程可见。 注意 volatile 仅仅保证了共享变量的可见性让其它线程能够看到最新值但不能解决指令交错问题不能保证原 子性 CAS 必须借助 volatile 才能读取到共享变量的最新值来实现【比较并交换】的效果。
为什么无锁效率高
无锁情况下即使重试失败线程始终在高速运行没有停歇类似于自旋。而 synchronized 会让线程在没有获得锁的时候发生上下文切换进入阻塞。线程的上下文切换是费时的在重试次数不是太多时无锁的效率高于有锁。线程就好像高速跑道上的赛车高速运行时速度超快一旦发生上下文切换就好比赛车要减速、熄火 等被唤醒又得重新打火、启动、加速… 恢复到高速运行代价比较大但无锁情况下因为线程要保持运行需要额外 CPU 的支持CPU 在这里就好比高速跑道没有额外的跑 道线程想高速运行也无从谈起虽然不会进入阻塞但由于没有分到时间片仍然会进入可运行状态还 是会导致上下文切换。所以总的来说当线程数小于等于cpu核心数时使用无锁方案是很合适的因为有足够多的cpu让线程运行。当线程数远多于cpu核心数时无锁效率相比于有锁就没有太大优势因为依旧会发生上下文切换。 CAS特点
结合 CAS 和 volatile 可以实现无锁并发适用于线程数少、多核 CPU 的场景下。
CAS 是基于乐观锁的思想乐观的估计不怕别的线程来修改共享变量就算改了也没关系我吃亏点再重试呗。synchronized 是基于悲观锁的思想悲观的估计得防着其它线程来修改共享变量我上了锁你们都别想改我改完了解开锁你们才有机会。CAS 体现的是无锁并发、无阻塞并发请仔细体会这两句话的意思 因为没有使用 synchronized所以线程不会陷入阻塞这是效率提升的因素之一但如果竞争激烈可以想到重试必然频繁发生反而效率会受影响
原子整数
J.U.C 并发包提供了
AtomicBooleanAtomicIntegerAtomicLong
以 AtomicInteger 为例
AtomicInteger i new AtomicInteger(0);
// 获取并自增i 0, 结果 i 1, 返回 0类似于 i
System.out.println(i.getAndIncrement());
// 自增并获取i 1, 结果 i 2, 返回 2类似于 i
System.out.println(i.incrementAndGet());
// 自减并获取i 2, 结果 i 1, 返回 1类似于 --i
System.out.println(i.decrementAndGet());
// 获取并自减i 1, 结果 i 0, 返回 1类似于 i--
System.out.println(i.getAndDecrement());
// 获取并加值i 0, 结果 i 5, 返回 0
System.out.println(i.getAndAdd(5));
// 加值并获取i 5, 结果 i 0, 返回 0
System.out.println(i.addAndGet(-5));
// 获取并更新i 0, p 为 i 的当前值, 结果 i -2, 返回 0
// 其中函数中的操作能保证原子但函数需要无副作用
System.out.println(i.getAndUpdate(p - p - 2));
// 更新并获取i -2, p 为 i 的当前值, 结果 i 0, 返回 0
// 其中函数中的操作能保证原子但函数需要无副作用
System.out.println(i.updateAndGet(p - p 2));
// 获取并计算i 0, p 为 i 的当前值, x 为参数1, 结果 i 10, 返回 0
// 其中函数中的操作能保证原子但函数需要无副作用
// getAndUpdate 如果在 lambda 中引用了外部的局部变量要保证该局部变量是 final 的
// getAndAccumulate 可以通过 参数1 来引用外部的局部变量但因为其不在 lambda 中因此不必是 final
System.out.println(i.getAndAccumulate(10, (p, x) - p x));
// 计算并获取i 10, p 为 i 的当前值, x 为参数1, 结果 i 0, 返回 0
// 其中函数中的操作能保证原子但函数需要无副作用
System.out.println(i.accumulateAndGet(-10, (p, x) - p x));说明 以上方法都是以CAS为基础进行了封装保证了方法的原子性和变量的可见性。 updateAndGet方法的手动实现 public static int updateAndGet(AtomicInteger i, IntUnaryOperator operator){while (true){int prev i.get();int next operator.applyAsInt(prev);if(i.compareAndSet(prev,next)){return next;}}
}原子引用
为什么需要原子引用类型
AtomicReferenceAtomicMarkableReferenceAtomicStampedReference
实际开发的过程中我们使用的不一定是int、long等基本数据类型也有可能时BigDecimal这样的类型这时就需要用到原子引用作为容器。原子引用设置值使用的是unsafe.compareAndSwapObject()方法。原子引用中表示数据的类型需要重写equals()方法。
有如下方法
public interface DecimalAccount {// 获取余额BigDecimal getBalance();// 取款void withdraw(BigDecimal amount);/*** 方法内会启动 1000 个线程每个线程做 -10 元 的操作* 如果初始余额为 10000 那么正确的结果应当是 0*/static void demo(DecimalAccount account) {ListThread ts new ArrayList();for (int i 0; i 1000; i) {ts.add(new Thread(() - {account.withdraw(BigDecimal.TEN);}));}ts.forEach(Thread::start);ts.forEach(t - {try {t.join();} catch (InterruptedException e) {e.printStackTrace();}});System.out.println(account.getBalance());}
}试着提供不同的 DecimalAccount 实现实现安全的取款操作
不安全实现
class DecimalAccountUnsafe implements DecimalAccount {BigDecimal balance;public DecimalAccountUnsafe(BigDecimal balance) {this.balance balance;}Overridepublic BigDecimal getBalance() {return balance;}Overridepublic void withdraw(BigDecimal amount) {BigDecimal balance this.getBalance();this.balance balance.subtract(amount);}
}安全实现-使用锁
class DecimalAccountSafeLock implements DecimalAccount {private final Object lock new Object();BigDecimal balance;public DecimalAccountSafeLock(BigDecimal balance) {this.balance balance;}Overridepublic BigDecimal getBalance() {return balance;}Overridepublic void withdraw(BigDecimal amount) {synchronized (lock) {BigDecimal balance this.getBalance();this.balance balance.subtract(amount);}}
}安全实现-使用 CAS
class DecimalAccountSafeCas implements DecimalAccount {AtomicReferenceBigDecimal ref;public DecimalAccountSafeCas(BigDecimal balance) {ref new AtomicReference(balance);}Overridepublic BigDecimal getBalance() {return ref.get();}Overridepublic void withdraw(BigDecimal amount) {while (true) {BigDecimal prev ref.get();BigDecimal next prev.subtract(amount);if (ref.compareAndSet(prev, next)) {break;}}}
}测试代码
DecimalAccount.demo(new DecimalAccountUnsafe(new BigDecimal(10000)));
DecimalAccount.demo(new DecimalAccountSafeLock(new BigDecimal(10000)));
DecimalAccount.demo(new DecimalAccountSafeCas(new BigDecimal(10000)));运行结果
4310 cost: 425 ms
0 cost: 285 ms
0 cost: 274 msABA 问题及解决
ABA 问题
static AtomicReferenceString ref new AtomicReference(A);
public static void main(String[] args) throws InterruptedException {log.debug(main start...);// 获取值 A// 这个共享变量被它线程修改过String prev ref.get();other();sleep(1);// 尝试改为 Clog.debug(change A-C {}, ref.compareAndSet(prev, C));
}
private static void other() {new Thread(() - {log.debug(change A-B {}, ref.compareAndSet(ref.get(), B));}, t1).start();sleep(0.5);new Thread(() - {log.debug(change B-A {}, ref.compareAndSet(ref.get(), A));}, t2).start();
}输出
11:29:52.325 c.Test36 [main] - main start...
11:29:52.379 c.Test36 [t1] - change A-B true
11:29:52.879 c.Test36 [t2] - change B-A true
11:29:53.880 c.Test36 [main] - change A-C true主线程仅能判断出共享变量的值与最初值 A 是否相同不能感知到这种从 A 改为 B 又 改回 A 的情况如果主线程 希望
只要有其它线程【动过了】共享变量那么自己的 cas 就算失败这时仅比较值是不够的需要再加一个版本号
AtomicStampedReference
static AtomicStampedReferenceString ref new AtomicStampedReference(A, 0);
public static void main(String[] args) throws InterruptedException {log.debug(main start...);// 获取值 AString prev ref.getReference();// 获取版本号int stamp ref.getStamp();log.debug(版本 {}, stamp);// 如果中间有其它线程干扰发生了 ABA 现象other();sleep(1);// 尝试改为 Clog.debug(change A-C {}, ref.compareAndSet(prev, C, stamp, stamp 1));
}
private static void other() {new Thread(() - {log.debug(change A-B {}, ref.compareAndSet(ref.getReference(), B, ref.getStamp(), ref.getStamp() 1));log.debug(更新版本为 {}, ref.getStamp());}, t1).start();sleep(0.5);new Thread(() - {log.debug(change B-A {}, ref.compareAndSet(ref.getReference(), A, ref.getStamp(), ref.getStamp() 1));log.debug(更新版本为 {}, ref.getStamp());}, t2).start();
}输出为
15:41:34.891 c.Test36 [main] - main start...
15:41:34.894 c.Test36 [main] - 版本 0
15:41:34.956 c.Test36 [t1] - change A-B true
15:41:34.956 c.Test36 [t1] - 更新版本为 1
15:41:35.457 c.Test36 [t2] - change B-A true
15:41:35.457 c.Test36 [t2] - 更新版本为 2
15:41:36.457 c.Test36 [main] - change A-C false AtomicStampedReference 可以给原子引用加上版本号追踪原子引用整个的变化过程如 A - B - A - C 通过AtomicStampedReference我们可以知道引用变量中途被更改了几次。
但是有时候并不关心引用变量更改了几次只是单纯的关心是否更改过所以就有了 AtomicMarkableReference
#mermaid-svg-Ze9Ih65K8rj353HE {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Ze9Ih65K8rj353HE .error-icon{fill:#552222;}#mermaid-svg-Ze9Ih65K8rj353HE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Ze9Ih65K8rj353HE .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-Ze9Ih65K8rj353HE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Ze9Ih65K8rj353HE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Ze9Ih65K8rj353HE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Ze9Ih65K8rj353HE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Ze9Ih65K8rj353HE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Ze9Ih65K8rj353HE .marker.cross{stroke:#333333;}#mermaid-svg-Ze9Ih65K8rj353HE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Ze9Ih65K8rj353HE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Ze9Ih65K8rj353HE .cluster-label text{fill:#333;}#mermaid-svg-Ze9Ih65K8rj353HE .cluster-label span{color:#333;}#mermaid-svg-Ze9Ih65K8rj353HE .label text,#mermaid-svg-Ze9Ih65K8rj353HE span{fill:#333;color:#333;}#mermaid-svg-Ze9Ih65K8rj353HE .node rect,#mermaid-svg-Ze9Ih65K8rj353HE .node circle,#mermaid-svg-Ze9Ih65K8rj353HE .node ellipse,#mermaid-svg-Ze9Ih65K8rj353HE .node polygon,#mermaid-svg-Ze9Ih65K8rj353HE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Ze9Ih65K8rj353HE .node .label{text-align:center;}#mermaid-svg-Ze9Ih65K8rj353HE .node.clickable{cursor:pointer;}#mermaid-svg-Ze9Ih65K8rj353HE .arrowheadPath{fill:#333333;}#mermaid-svg-Ze9Ih65K8rj353HE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Ze9Ih65K8rj353HE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Ze9Ih65K8rj353HE .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-Ze9Ih65K8rj353HE .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-Ze9Ih65K8rj353HE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Ze9Ih65K8rj353HE .cluster text{fill:#333;}#mermaid-svg-Ze9Ih65K8rj353HE .cluster span{color:#333;}#mermaid-svg-Ze9Ih65K8rj353HE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Ze9Ih65K8rj353HE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}倒空检查已满还空保洁阿姨主人垃圾袋新垃圾袋AtomicMarkableReference
AtomicStampedReference 可以给原子引用加上版本号追踪原子引用整个的变化过程如 A - B - A - C 通过AtomicStampedReference我们可以知道引用变量中途被更改了几次。 但是有时候并不关心引用变量更改了几次只是单纯的关心是否更改过所以就有了 AtomicMarkableReference
class GarbageBag {String desc;public GarbageBag(String desc) {this.desc desc;}public void setDesc(String desc) {this.desc desc;}Overridepublic String toString() {return super.toString() desc;}
}Slf4j
public class TestABAAtomicMarkableReference {public static void main(String[] args) throws InterruptedException {GarbageBag bag new GarbageBag(装满了垃圾);// 参数2 mark 可以看作一个标记表示垃圾袋满了AtomicMarkableReferenceGarbageBag ref new AtomicMarkableReference(bag, true);log.debug(主线程 start...);GarbageBag prev ref.getReference();log.debug(prev.toString());new Thread(() - {log.debug(打扫卫生的线程 start...);bag.setDesc(空垃圾袋);while (!ref.compareAndSet(bag, bag, true, false)) {}log.debug(bag.toString());}).start();Thread.sleep(1000);log.debug(主线程想换一只新垃圾袋);boolean success ref.compareAndSet(prev, new GarbageBag(空垃圾袋), true, false);log.debug(换了么 success);log.debug(ref.getReference().toString());}
}输出
2019-10-13 15:30:09.264 [main] 主线程 start...
2019-10-13 15:30:09.270 [main] cn.itcast.GarbageBag5f0fd5a0 装满了垃圾
2019-10-13 15:30:09.293 [Thread-1] 打扫卫生的线程 start...
2019-10-13 15:30:09.294 [Thread-1] cn.itcast.GarbageBag5f0fd5a0 空垃圾袋
2019-10-13 15:30:10.294 [main] 主线程想换一只新垃圾袋
2019-10-13 15:30:10.294 [main] 换了么false
2019-10-13 15:30:10.294 [main] cn.itcast.GarbageBag5f0fd5a0 空垃圾袋可以注释掉打扫卫生线程代码再观察输出
两者的区别 AtomicStampedReference 需要我们传入整型变量作为版本号来判定是否被更改过 AtomicMarkableReference需要我们传入布尔变量作为标记来判断是否被更改过
原子数组
AtomicIntegerArrayAtomicLongArrayAtomicReferenceArray
有如下方法
/**参数1提供数组、可以是线程不安全数组或线程安全数组参数2获取数组长度的方法参数3自增方法回传 array, index参数4打印数组的方法
*/
// supplier 提供者 无中生有 ()-结果
// function 函数 一个参数一个结果 (参数)-结果 , BiFunction (参数1,参数2)-结果
// consumer 消费者 一个参数没结果 (参数)-void, BiConsumer (参数1,参数2)-
private static T void demo(SupplierT arraySupplier,FunctionT, Integer lengthFun,BiConsumerT, Integer putConsumer,ConsumerT printConsumer ) {ListThread ts new ArrayList();T array arraySupplier.get();int length lengthFun.apply(array);for (int i 0; i length; i) {// 每个线程对数组作 10000 次操作ts.add(new Thread(() - {for (int j 0; j 10000; j) {putConsumer.accept(array, j%length);}}));}ts.forEach(t - t.start()); // 启动所有线程ts.forEach(t - {try {t.join();} catch (InterruptedException e) {e.printStackTrace();}}); // 等所有线程结束printConsumer.accept(array);
}不安全的数组
demo(()-new int[10],(array)-array.length,(array, index) - array[index],array- System.out.println(Arrays.toString(array))
);结果
[9870, 9862, 9774, 9697, 9683, 9678, 9679, 9668, 9680, 9698] 安全的数组
demo(()- new AtomicIntegerArray(10),(array) - array.length(),(array, index) - array.getAndIncrement(index),array - System.out.println(array)
);结果
[10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000] 原子(字段)更新器
AtomicReferenceFieldUpdater // 域 字段AtomicIntegerFieldUpdaterAtomicLongFieldUpdater
利用字段更新器可以针对对象的某个域Field进行原子操作只能配合 volatile 修饰的字段使用否则会出现 异常
Exception in thread main java.lang.IllegalArgumentException: Must be volatile typepublic class Test5 {private volatile int field;public static void main(String[] args) {AtomicIntegerFieldUpdater fieldUpdater AtomicIntegerFieldUpdater.newUpdater(Test5.class, field);Test5 test5 new Test5();fieldUpdater.compareAndSet(test5, 0, 10);// 修改成功 field 10System.out.println(test5.field);// 修改成功 field 20fieldUpdater.compareAndSet(test5, 10, 20);System.out.println(test5.field);// 修改失败 field 20fieldUpdater.compareAndSet(test5, 10, 30);System.out.println(test5.field);}
}输出
10
20
20 原子累加器
我们使用前面所说原子整数也可以进行累加操作不过JDK8之后提供了几个专门用来做累加的类使用这些类做累加的性能更高 private static T void demo(SupplierT adderSupplier, ConsumerT action) {T adder adderSupplier.get();long start System.nanoTime();ListThread ts new ArrayList();// 4 个线程每人累加 50 万for (int i 0; i 40; i) {ts.add(new Thread(() - {for (int j 0; j 500000; j) {action.accept(adder);}}));}ts.forEach(t - t.start());ts.forEach(t - {try {t.join();} catch (InterruptedException e) {e.printStackTrace();}});long end System.nanoTime();System.out.println(adder cost: (end - start)/1000_000);
}比较 AtomicLong 与 LongAdder
for (int i 0; i 5; i) {demo(() - new LongAdder(), adder - adder.increment());
}
for (int i 0; i 5; i) {demo(() - new AtomicLong(), adder - adder.getAndIncrement());
}输出
1000000 cost:43
1000000 cost:9
1000000 cost:7
1000000 cost:7
1000000 cost:7
1000000 cost:31
1000000 cost:27
1000000 cost:28
1000000 cost:24
1000000 cost:22 执行代码后发现使用 LongAdder 比 AtomicLong 快23倍使用 LongAdder 性能提升的原因很简单就是在有竞争时设置多个累加单元(但不会超过cpu的核心数)Therad-0 累加 Cell[0]而 Thread-1 累加Cell[1]… 最后将结果汇总。这样它们在累加时操作的不同的 Cell 变量因此减少了 CAS 重试失败从而提高性能。 LongAddr的原理可以参考下面的文章 Java 并发编程中篇 Unsafe
Unsafe 对象提供了非常底层的操作内存、线程的方法Unsafe 对象不能直接调用只能通过反射获得。jdk8直接调用Unsafe.getUnsafe()获得的unsafe不能用。 CAS、park、unpark等其底层调用的都是Unsafe的方法。这个名字不是说会引发线程的安全问题不安全unsafe而是说操作的太过底层不建议编程人员直接使用。 我们通过反射编写一个工具类来拿到unsafe
public class UnsafeAccessor {static Unsafe unsafe;static {try { Field theUnsafe Unsafe.class.getDeclaredField(theUnsafe);theUnsafe.setAccessible(true);unsafe (Unsafe) theUnsafe.get(null);} catch (NoSuchFieldException | IllegalAccessException e) {throw new Error(e);}}static Unsafe getUnsafe() {return unsafe;}
}Unsafe CAS 操作
unsafe实现字段更新
Data
class Student {volatile int id;volatile String name;
}Unsafe unsafe UnsafeAccessor.getUnsafe();
Field id Student.class.getDeclaredField(id);
Field name Student.class.getDeclaredField(name);
// 获得成员变量的偏移量
long idOffset UnsafeAccessor.unsafe.objectFieldOffset(id);
long nameOffset UnsafeAccessor.unsafe.objectFieldOffset(name);
Student student new Student();
// 使用 cas 方法替换成员变量的值
UnsafeAccessor.unsafe.compareAndSwapInt(student, idOffset, 0, 20); // 返回 true
UnsafeAccessor.unsafe.compareAndSwapObject(student, nameOffset, null, 张三); // 返回 true
System.out.println(student);输出
Student(id20, name张三) unsafe实现原子整数
class AtomicData {private volatile int data;static final Unsafe unsafe;static final long DATA_OFFSET;static {unsafe UnsafeAccessor.getUnsafe();try {// data 属性在 DataContainer 对象中的偏移量用于 Unsafe 直接访问该属性DATA_OFFSET unsafe.objectFieldOffset(AtomicData.class.getDeclaredField(data));} catch (NoSuchFieldException e) {throw new Error(e);}}public AtomicData(int data) {this.data data;}public void decrease(int amount) {int oldValue;while(true) {// 获取共享变量旧值可以在这一行加入断点修改 data 调试来加深理解oldValue data;// cas 尝试修改 data 为 旧值 amount如果期间旧值被别的线程改了返回 falseif (unsafe.compareAndSwapInt(this, DATA_OFFSET, oldValue, oldValue - amount)) {return;}}}public int getData() {return data;}
}Account 实现
Account.demo(new Account() {AtomicData atomicData new AtomicData(10000);Overridepublic Integer getBalance() {return atomicData.getData();}Overridepublic void withdraw(Integer amount) {atomicData.decrease(amount);}
});
文章转载自: http://www.morning.btsls.cn.gov.cn.btsls.cn http://www.morning.nqpxs.cn.gov.cn.nqpxs.cn http://www.morning.bbrf.cn.gov.cn.bbrf.cn http://www.morning.fyskq.cn.gov.cn.fyskq.cn http://www.morning.gcqs.cn.gov.cn.gcqs.cn http://www.morning.fwblh.cn.gov.cn.fwblh.cn http://www.morning.tnbas.com.gov.cn.tnbas.com http://www.morning.lxqyf.cn.gov.cn.lxqyf.cn http://www.morning.txmkx.cn.gov.cn.txmkx.cn http://www.morning.wrdlf.cn.gov.cn.wrdlf.cn http://www.morning.mkczm.cn.gov.cn.mkczm.cn http://www.morning.fstesen.com.gov.cn.fstesen.com http://www.morning.c7617.cn.gov.cn.c7617.cn http://www.morning.znqxt.cn.gov.cn.znqxt.cn http://www.morning.rksg.cn.gov.cn.rksg.cn http://www.morning.nynyj.cn.gov.cn.nynyj.cn http://www.morning.kdrly.cn.gov.cn.kdrly.cn http://www.morning.fmznd.cn.gov.cn.fmznd.cn http://www.morning.wmqrn.cn.gov.cn.wmqrn.cn http://www.morning.dpgdj.cn.gov.cn.dpgdj.cn http://www.morning.yjtnc.cn.gov.cn.yjtnc.cn http://www.morning.rxcqt.cn.gov.cn.rxcqt.cn http://www.morning.ttfh.cn.gov.cn.ttfh.cn http://www.morning.flmxl.cn.gov.cn.flmxl.cn http://www.morning.ksjmt.cn.gov.cn.ksjmt.cn http://www.morning.xfjwm.cn.gov.cn.xfjwm.cn http://www.morning.qymqh.cn.gov.cn.qymqh.cn http://www.morning.gtmdq.cn.gov.cn.gtmdq.cn http://www.morning.ypcbm.cn.gov.cn.ypcbm.cn http://www.morning.hqbnx.cn.gov.cn.hqbnx.cn http://www.morning.dwfxl.cn.gov.cn.dwfxl.cn http://www.morning.madamli.com.gov.cn.madamli.com http://www.morning.tzrmp.cn.gov.cn.tzrmp.cn http://www.morning.bwkhp.cn.gov.cn.bwkhp.cn http://www.morning.gtjkh.cn.gov.cn.gtjkh.cn http://www.morning.fqmbt.cn.gov.cn.fqmbt.cn http://www.morning.kxqwg.cn.gov.cn.kxqwg.cn http://www.morning.zpnfc.cn.gov.cn.zpnfc.cn http://www.morning.gqtw.cn.gov.cn.gqtw.cn http://www.morning.dxhnm.cn.gov.cn.dxhnm.cn http://www.morning.yhywx.cn.gov.cn.yhywx.cn http://www.morning.dbnrl.cn.gov.cn.dbnrl.cn http://www.morning.ydgzj.cn.gov.cn.ydgzj.cn http://www.morning.rwfp.cn.gov.cn.rwfp.cn http://www.morning.rfwqt.cn.gov.cn.rfwqt.cn http://www.morning.rzcbk.cn.gov.cn.rzcbk.cn http://www.morning.bktzr.cn.gov.cn.bktzr.cn http://www.morning.yfmlj.cn.gov.cn.yfmlj.cn http://www.morning.rkxk.cn.gov.cn.rkxk.cn http://www.morning.gxtfk.cn.gov.cn.gxtfk.cn http://www.morning.pghfy.cn.gov.cn.pghfy.cn http://www.morning.xbxks.cn.gov.cn.xbxks.cn http://www.morning.mzbyl.cn.gov.cn.mzbyl.cn http://www.morning.rxhsm.cn.gov.cn.rxhsm.cn http://www.morning.xiaobaixinyong.cn.gov.cn.xiaobaixinyong.cn http://www.morning.cyhlq.cn.gov.cn.cyhlq.cn http://www.morning.qfcnp.cn.gov.cn.qfcnp.cn http://www.morning.znsyn.cn.gov.cn.znsyn.cn http://www.morning.qrmyd.cn.gov.cn.qrmyd.cn http://www.morning.rgmls.cn.gov.cn.rgmls.cn http://www.morning.rwls.cn.gov.cn.rwls.cn http://www.morning.kxltf.cn.gov.cn.kxltf.cn http://www.morning.xq3nk42mvv.cn.gov.cn.xq3nk42mvv.cn http://www.morning.lbywt.cn.gov.cn.lbywt.cn http://www.morning.rszwc.cn.gov.cn.rszwc.cn http://www.morning.wftrs.cn.gov.cn.wftrs.cn http://www.morning.ktpzb.cn.gov.cn.ktpzb.cn http://www.morning.pslzp.cn.gov.cn.pslzp.cn http://www.morning.yhwmg.cn.gov.cn.yhwmg.cn http://www.morning.schwr.cn.gov.cn.schwr.cn http://www.morning.zrgx.cn.gov.cn.zrgx.cn http://www.morning.mzwqt.cn.gov.cn.mzwqt.cn http://www.morning.qkcyk.cn.gov.cn.qkcyk.cn http://www.morning.hdrsr.cn.gov.cn.hdrsr.cn http://www.morning.fhykt.cn.gov.cn.fhykt.cn http://www.morning.qtnmp.cn.gov.cn.qtnmp.cn http://www.morning.pjbhk.cn.gov.cn.pjbhk.cn http://www.morning.mxcgf.cn.gov.cn.mxcgf.cn http://www.morning.zpqbh.cn.gov.cn.zpqbh.cn http://www.morning.gmplp.cn.gov.cn.gmplp.cn