(十二) 共享模型之无锁【原子整数、原子引用、原子数组】
创始人
2024-03-19 08:20:27
0

一、原子整数

J.U.C 并发包提供了:

AtomicBoolean

AtomicInteger

AtomicLong

public class Test34 {public static void main(String[] args) {AtomicInteger i = new AtomicInteger(0);// 获取并自增(i = 0, 结果 i = 1, 返回 0),类似于 i++System.out.println(i.getAndIncrement());// 自增并获取(i = 1, 结果 i = 2, 返回 2),类似于 ++iSystem.out.println(i.incrementAndGet());// 自减并获取(i = 2, 结果 i = 1, 返回 1),类似于 --iSystem.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 中因此不必是 finalSystem.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));}
}

二、原子引用

J.U.C 并发包提供了:

AtomicReference

AtomicMarkableReference

AtomicStampedReference

1. 安全实现-使用 CAS

@Slf4j(topic = "c.Test35")
public class Test35 {public static void main(String[] args) {DecimalAccount.demo(new DecimalAccountCas(new BigDecimal("10000")));}
}class DecimalAccountCas implements DecimalAccount {private AtomicReference balance;public DecimalAccountCas(BigDecimal balance) {
//        this.balance = balance;this.balance = new AtomicReference<>(balance);}@Overridepublic BigDecimal getBalance() {return balance.get();}@Overridepublic void withdraw(BigDecimal amount) {while(true) {BigDecimal prev = balance.get();BigDecimal next = prev.subtract(amount);if (balance.compareAndSet(prev, next)) {break;}}}
}interface DecimalAccount {// 获取余额BigDecimal getBalance();// 取款void withdraw(BigDecimal amount);/*** 方法内会启动 1000 个线程,每个线程做 -10 元 的操作* 如果初始余额为 10000 那么正确的结果应当是 0*/static void demo(DecimalAccount account) {List 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());}
}

2. ABA 问题及解决

@Slf4j(topic = "c.Test36")
public class Test36 {static AtomicReference ref = new AtomicReference<>("A");public static void main(String[] args) throws InterruptedException {log.debug("main start...");// 获取值 A// 这个共享变量被其他线程修改过String prev = ref.get();other();sleep(5);// 尝试改为 Clog.debug("change A->C {}", ref.compareAndSet(prev, "C"));}private static void other() throws InterruptedException {new Thread(() -> {log.debug("change A->B", ref.compareAndSet(ref.get() ,"B"));}, "t1").start();sleep( 1);new Thread(() -> {log.debug("change B->A", ref.compareAndSet(ref.get() ,"A"));}, "t2").start();}
}
主线程仅能判断出共享变量的值与最初值 A 是否相同,不能感知到这种从 A 改为 B 又 改回 A 的情况,如果主线程希望: 只要有其它线程【动过了】共享变量,那么自己的 cas 就算失败,这时,仅比较值是不够的,需要再加一个版本号

3. AtomicStampedReference

@Slf4j(topic = "c.Test36")
public class Test36 {static AtomicStampedReference 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();}
}

4. AtomicMarkableReference

但是有时候,并不关心引用变量更改了几次,只是单纯的关心是否更改过,所以就有了 AtomicMarkableReference
@Slf4j(topic = "c.Test38")
public class Test38 {public static void main(String[] args) throws InterruptedException {GarbageBag bag = new GarbageBag("装满了垃圾");// 参数2 mark 可以看作一个标记,表示垃圾袋满了AtomicMarkableReference ref = new AtomicMarkableReference<>(bag, true);log.debug("start...");GarbageBag prev = ref.getReference();log.debug(prev.toString());new Thread(() -> {log.debug("start...");bag.setDesc("空垃圾袋");ref.compareAndSet(bag, bag, true, false);log.debug(bag.toString());},"保洁阿姨").start();sleep(1);log.debug("想换一只新垃圾袋?");boolean success = ref.compareAndSet(prev, new GarbageBag("空垃圾袋"), true, false);log.debug("换了么?" + success);log.debug(ref.getReference().toString());}
}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;}
}

三、原子数组

J.U.C 并发包提供了:

AtomicIntegerArray

AtomicLongArray

AtomicReferenceArray

public class Test39 {public static void main(String[] args) {demo(()->new int[10],(array)->array.length,(array, index) -> array[index]++,array-> System.out.println(Arrays.toString(array)));demo(()-> new AtomicIntegerArray(10),(array) -> array.length(),(array, index) -> array.getAndIncrement(index),array -> System.out.println(array));}/**参数1,提供数组、可以是线程不安全数组或线程安全数组参数2,获取数组长度的方法参数3,自增方法,回传 array, index参数4,打印数组的方法*/// supplier 提供者 无中生有  ()->结果// function 函数   一个参数一个结果   (参数)->结果  ,  BiFunction (参数1,参数2)->结果// consumer 消费者 一个参数没结果  (参数)->void,      BiConsumer (参数1,参数2)->private static  void demo(Supplier arraySupplier,Function lengthFun,BiConsumer putConsumer,Consumer printConsumer ) {List 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);}
}

相关内容

热门资讯

景顺长城优质成长股票C净值上涨... 景顺长城优质成长股票型证券投资基金(简称:景顺长城优质成长股票C,代码021500)公布7月15日最...
中邮健康文娱灵活配置混合C净值... 中邮健康文娱灵活配置混合型证券投资基金(简称:中邮健康文娱灵活配置混合C,代码022252)公布7月...
富国中证港股通互联网ETF净值... 富国中证港股通互联网交易型开放式指数证券投资基金(简称:富国中证港股通互联网ETF,代码159792...
永赢科技驱动C净值上涨3.47... 永赢科技驱动混合型证券投资基金(简称:永赢科技驱动C,代码008920)公布7月15日最新净值,上涨...
信澳匠心严选一年持有期混合A净... 信澳匠心严选一年持有期混合型证券投资基金(简称:信澳匠心严选一年持有期混合A,代码016372)公布...
华商智能生活灵活配置混合C净值... 华商智能生活灵活配置混合型证券投资基金(简称:华商智能生活灵活配置混合C,代码015385)公布7月...
华商卓越成长一年持有混合A净值... 华商卓越成长一年持有期混合型证券投资基金(简称:华商卓越成长一年持有混合A,代码014350)公布7...
平安品质优选混合C净值上涨5.... 平安品质优选混合型证券投资基金(简称:平安品质优选混合C,代码014461)公布7月15日最新净值,...
以色列国防军宣布一系列高级军官... 每经AI快讯,7月16日,以色列国防军总参谋长扎米尔宣布了一系列有关以军总参谋部调整和高级军官任命的...
博时优质精选混合C净值上涨3.... 博时优质精选混合型证券投资基金(简称:博时优质精选混合C,代码015903)公布7月15日最新净值,...