当前位置: 首页 > news >正文

徐水网站建设做推广怎么做

徐水网站建设,做推广怎么做,个人简历免费模板,国内最最早做虚拟货币的网站JUC并发编程系列文章 http://t.csdn.cn/UgzQi 文章目录JUC并发编程系列文章前言一、问题的引出如何保证取款方法的线程安全解决方案一、使用synchronized锁住临界区代码解决方案二、无锁(AtomicInteger 原子整数类)二、CAS 与 volatileAtomicInteger . compareAndSet( ) 方法的…

JUC并发编程系列文章

http://t.csdn.cn/UgzQi


文章目录

  • JUC并发编程系列文章
  • 前言
  • 一、问题的引出
    • 如何保证取款方法的线程安全
    • 解决方案一、使用synchronized锁住临界区代码
    • 解决方案二、无锁(AtomicInteger 原子整数类)
  • 二、CAS 与 volatile
    • AtomicInteger . compareAndSet( ) 方法的工作方式
    • 为什么 CAS 无锁效率更高
    • CAS的特点
  • 三、原子整数
    • 以 AtomicInteger 为粒
  • 四、原子引用 P169
    • ABA 问题及解决
      • AtomicStampedReference(维护版本号)
      • AtomicMarkableReference(仅维护是否修改过)
  • 五、原子数组
  • 六、字段更新器
  • 七、原子累加器
    • 1、累加器性能比较
    • 2、LongAdder源码🔞
    • 3、Cell原理之伪共享
    • 4、LongAdder. add( )源码
    • 5、longAccumulate源码
    • 6、sum源码
  • 八、(底层类)Unsafe
    • 通过反射获取Unsafe对象
    • Unsafe CAS 操作
    • 模拟实现原子整数
  • 总结


前言


在这里插入图片描述

一、问题的引出

如何保证取款方法的线程安全

如何保证取款方法的线程安全
有如下需求,保证 account.withdraw 取款方法的线程安全

import java.util.ArrayList;
import java.util.List;public class testThread19 {public static void main(String[] args) {Account account = new AccountUnsafe(10000);Account.demo(account);/*** 输出* 390 cost: 104 ms  应该输出 0,所以是线程不安全的*/}
}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;}
}interface Account {// 获取余额Integer getBalance();// 取款void withdraw(Integer amount);/*** 方法内会启动 1000 个线程,每个线程做 -10 元 的操作* 如果初始余额为 10000 那么正确的结果应当是 0*/static void demo(Account account) {List<Thread> 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");}
}

解决方案一、使用synchronized锁住临界区代码

在这里插入图片描述

解决方案二、无锁(AtomicInteger 原子整数类)

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;public class testThread19 {public static void main(String[] args) {Account account = new AccountSafe(10000);Account.demo(account);/*** 输出*  0 cost: 97 ms 线程安全*/}
}/*** AtomicInteger 原子整数 解决方案*/
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);}
}/*** synchronized 解决方案*/
class AccountUnsafe implements Account {//  Integer 整数private Integer balance;public AccountUnsafe(Integer balance) {this.balance = balance;}@Overridepublic synchronized Integer getBalance() {return balance;}@Overridepublic synchronized void withdraw(Integer amount) {balance -= amount;}
}interface Account {// 获取余额Integer getBalance();// 取款void withdraw(Integer amount);/*** 方法内会启动 1000 个线程,每个线程做 -10 元 的操作* 如果初始余额为 10000 那么正确的结果应当是 0*/static void demo(Account account) {List<Thread> 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");}
}

二、CAS 与 volatile

在这里插入图片描述

AtomicInteger . compareAndSet( ) 方法的工作方式

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

为什么 CAS 无锁效率更高

在这里插入图片描述

CAS的特点

在这里插入图片描述

三、原子整数

在这里插入图片描述

以 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));

四、原子引用 P169

在这里插入图片描述

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;public class testThread20 {public static void main(String[] args) {DecimalAccount account = new DecimalAccountSafeCas(new BigDecimal("10000"));DecimalAccount.demo(account);}
}class DecimalAccountSafeCas implements DecimalAccount {//原子引用类AtomicReference<BigDecimal> 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;}}}}interface DecimalAccount {// 获取余额BigDecimal getBalance();// 取款void withdraw(BigDecimal amount);/*** 方法内会启动 1000 个线程,每个线程做 -10 元 的操作* 如果初始余额为 10000 那么正确的结果应当是 0*/static void demo(DecimalAccount account) {List<Thread> 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());}}

ABA 问题及解决

static AtomicReference<String> 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 AtomicStampedReference<String> 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(仅维护是否修改过)

但是有时候,并不关心引用变量更改了几次,只是单纯的关心是否更改过,所以就有了 AtomicMarkableReference

在这里插入图片描述

import lombok.extern.slf4j.Slf4j;import java.util.concurrent.atomic.AtomicMarkableReference;@Slf4j(topic = "c.testTread21")
public class testTread21 {public static void main(String[] args) throws InterruptedException {GarbageBag bag = new GarbageBag("装满了垃圾");// 参数2 mark 可以看作一个标记,表示垃圾袋满了AtomicMarkableReference<GarbageBag> 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());}
}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;}}
输出
14:27:38 [main] c.testTread21 - 主线程 start...
14:27:38 [main] c.testTread21 - GarbageBag@6fdb1f78 装满了垃圾
14:27:38 [Thread-0] c.testTread21 - 打扫卫生的线程 start...
14:27:38 [Thread-0] c.testTread21 - GarbageBag@6fdb1f78 空垃圾袋
14:27:39 [main] c.testTread21 - 主线程想换一只新垃圾袋?
14:27:39 [main] c.testTread21 - 换了么?false
14:27:39 [main] c.testTread21 - GarbageBag@6fdb1f78 空垃圾袋

五、原子数组

在这里插入图片描述


import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;public class testThread22 {public static void main(String[] args) {//不安全的数组输出//[8013, 7989, 7991, 7994, 7987, 8016, 8024, 7977, 8019, 8001]demo(()->new int[10],(array)->array.length,(array, index) -> array[index]++,array-> System.out.println(Arrays.toString(array)));//原子数组输出//[10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000]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 <T> void demo(Supplier<T> arraySupplier,Function<T, Integer> lengthFun,BiConsumer<T, Integer> putConsumer,Consumer<T> printConsumer ) {List<Thread> 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);}
}

六、字段更新器

利用字段更新器,可以针对对象的某个域(Field)进行原子操作,只能配合 volatile 修饰的字段使用,
否则会出现异常 Exception in thread “main” java.lang.IllegalArgumentException: Must be volatile type

在这里插入图片描述

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;public class testThread23 {public static void main(String[] args) {Student student = new Student();AtomicReferenceFieldUpdater fieldUpdater =AtomicReferenceFieldUpdater.newUpdater(Student.class,String.class,"name");boolean b = fieldUpdater.compareAndSet(student, null, "李四");System.out.println(b);System.out.println(student);/*** 输出* true* Student{name='李四'}*/}
}class Student{volatile  String name;@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +'}';}
}

七、原子累加器

1、累加器性能比较

在这里插入图片描述


import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.Consumer;
import java.util.function.Supplier;/**输出* LongAdder 都以及到 两位数了,AtomicLong还是三位数*LongAdder----------开始20000000 cost:8720000000 cost:2720000000 cost:2620000000 cost:2920000000 cost:37LongAdder----------结束20000000 cost:47420000000 cost:56220000000 cost:43220000000 cost:54320000000 cost:446*/
public class testThread23 {public static void main(String[] args) {System.out.println("LongAdder----------开始");for (int i = 0; i < 5; i++) {demo(() -> new LongAdder(), adder -> adder.increment());}System.out.println("LongAdder----------结束");for (int i = 0; i < 5; i++) {demo(() -> new AtomicLong(), adder -> adder.getAndIncrement());}}private static <T> void demo(Supplier<T> adderSupplier, Consumer<T> action) {T adder = adderSupplier.get();long start = System.nanoTime();List<Thread> 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);}
}

性能提升的原因很简单,就是在有竞争时,设置多个累加单元,Therad-0 累加 Cell[0],而 Thread-1 累加Cell[1]... 最后将结果汇总。 这样它们在累加时操作的不同的 Cell 变量,因此减少了 CAS 重试失败,从而提高性能。cell会监视线程之间的竞争,从最开始的两个累加单元到最大为cpu的核心数。而AtomicLong这种不是特定的原子类的累加单元是一个光杆司令遇到false循环重试,自然也就没有 LongAdder 的效率高。

2、LongAdder源码🔞

在这里插入图片描述

(cellsBusy作为)cas 锁(实现示例):在 cells 创建或扩容时, 置为 1, 表示加锁,cas 锁,当对线程进来对 cells数组进行创建或者扩容时,只能让一个线程进来构建这个数组,如果多个线程都来创建这个数组就乱套了,这时就需要加上 cas 锁,原本的标记为 0,第一个线程 拿着 0 和原来 的 0 比较,为 true 修改标记为 1,这时其他线程再拿着 0 过来和 标记 1 ,对比就不成立了,只能进行等待。

import lombok.extern.slf4j.Slf4j;import java.util.concurrent.atomic.AtomicInteger;// 不要用于实践!!!
@Slf4j(topic = "c.LockCas")
public class LockCas {private AtomicInteger state = new AtomicInteger(0);public void lock() {while (true) {if (state.compareAndSet(0, 1)) {break;}}}public void unlock() {log.debug("unlock...");state.set(0);}public static void main(String[] args) {LockCas lock = new LockCas();new Thread(() -> {log.debug("begin...");lock.lock();try {log.debug("lock...");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}} finally {lock.unlock();}}).start();new Thread(() -> {log.debug("begin...");lock.lock();try {log.debug("lock...");} finally {lock.unlock();}}).start();}
}输出
17:37:14 [Thread-0] c.LockCas - begin...
17:37:14 [Thread-1] c.LockCas - begin...
17:37:14 [Thread-0] c.LockCas - lock...
17:37:15 [Thread-0] c.LockCas - unlock...
17:37:15 [Thread-1] c.LockCas - lock...
17:37:15 [Thread-1] c.LockCas - unlock...

3、Cell原理之伪共享

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4、LongAdder. add( )源码

public void add(long x) {// as 为累加单元数组// b 为基础值// x 为累加值Cell[] as; long b, v; int m; Cell a;// 进入 if 的两个条件// 1. as 有值, 表示已经发生过竞争, 进入 if// 2. cas 给 base 累加时失败了, 表示 base 发生了竞争, 进入 ifif ((as = cells) != null || !casBase(b = base, b + x)) {// uncontended 表示 cell 没有竞争boolean uncontended = true;if (// as 还没有创建as == null || (m = as.length - 1) < 0 ||// 当前线程对应的 cell 还没有(a = as[getProbe() & m]) == null ||// cas 给当前线程的 cell 累加失败 uncontended=false ( a 为当前线程的 cell )!(uncontended = a.cas(v = a.value, v + x))) {// 进入 cell 数组创建、cell 创建的流程longAccumulate(x, null, uncontended);}}
}

add 流程图
在这里插入图片描述

5、longAccumulate源码


final void longAccumulate(long x, LongBinaryOperator fn,boolean wasUncontended) {int h;// 当前线程还没有对应的 cell, 需要随机生成一个 h 值用来将当前线程绑定到 cellif ((h = getProbe()) == 0) {// 初始化 probeThreadLocalRandom.current();// h 对应新的 probe 值, 用来对应 cellh = getProbe();wasUncontended = true;}// collide 为 true 表示需要扩容boolean collide = false; for (;;) {Cell[] as; Cell a; int n; long v;// 已经有了 cellsif ((as = cells) != null && (n = as.length) > 0) {// 还没有 cellif ((a = as[(n - 1) & h]) == null) {// 为 cellsBusy 加锁, 创建 cell, cell 的初始累加值为 x// 成功则 break, 否则继续 continue 循环}// 有竞争, 改变线程对应的 cell 来重试 caselse if (!wasUncontended)wasUncontended = true;// cas 尝试累加, fn 配合 LongAccumulator 不为 null, 配合 LongAdder 为 nullelse if (a.cas(v = a.value, ((fn == null) ? v + x : fn.applyAsLong(v, x))))break;// 如果 cells 长度已经超过了最大长度, 或者已经扩容, 改变线程对应的 cell 来重试 caselse if (n >= NCPU || cells != as)collide = false;// 确保 collide 为 false 进入此分支, 就不会进入下面的 else if 进行扩容了else if (!collide)collide = true;// 加锁else if (cellsBusy == 0 && casCellsBusy()) {// 加锁成功, 扩容continue;}// 改变线程对应的 cellh = advanceProbe(h);}// 还没有 cells, 尝试给 cellsBusy 加锁else if (cellsBusy == 0 && cells == as && casCellsBusy()) {// 加锁成功, 初始化 cells, 最开始长度为 2, 并填充一个 cell// 成功则 break;}// 上两种情况失败, 尝试给 base 累加else if (casBase(v = base, ((fn == null) ? v + x : fn.applyAsLong(v, x))))break;}
}

longAccumulate 流程图
在这里插入图片描述
在这里插入图片描述
每个线程刚进入 longAccumulate 时,会尝试对应一个 cell 对象(找到一个坑位)

在这里插入图片描述

6、sum源码

获取最终结果通过 sum 方法

public long sum() {Cell[] as = cells; Cell a;long sum = base;if (as != null) {for (int i = 0; i < as.length; ++i) {if ((a = as[i]) != null)sum += a.value;}}return sum; 
}

八、(底层类)Unsafe

取名Unsafe并不是说该类不安全,而是因为该类直接操作内存,比较复杂,意在告诉程序员使用该类有较大风险
概述
Unsafe 对象提供了非常底层的,操作内存、线程的方法,Unsafe 对象不能直接调用,只能通过反射获得

通过反射获取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对象的工具类

import sun.misc.Unsafe;import java.lang.reflect.Field;public class UnsafeAccessor {private static final Unsafe unsafe;static {try {Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);unsafe = (Unsafe) theUnsafe.get(null);} catch (NoSuchFieldException | IllegalAccessException e) {//这里必须 throw 出去throw new Error(e);}}private static Unsafe getUnsafe(){return unsafe;}
}

Unsafe CAS 操作

在这里插入图片描述


import lombok.Data;
import sun.misc.Unsafe;import java.lang.reflect.Field;public class TestUnsafeCAS {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);Unsafe unsafe = (Unsafe) theUnsafe.get(null);
//        Unsafe unsafe = UnsafeAccessor.getUnsafe();System.out.println(unsafe);// 1. 获取域的偏移地址long idOffset = unsafe.objectFieldOffset(Teacher.class.getDeclaredField("id"));long nameOffset = unsafe.objectFieldOffset(Teacher.class.getDeclaredField("name"));Teacher t = new Teacher();System.out.println(t);// 2. 执行 cas 操作unsafe.compareAndSwapInt(t, idOffset, 0, 1);unsafe.compareAndSwapObject(t, nameOffset, null, "张三");// 3. 验证System.out.println(t);}
}@Data
class Teacher {volatile int id;volatile String name;
}输出sun.misc.Unsafe@45ee12a7
Teacher(id=0, name=null)
Teacher(id=1, name=张三)

模拟实现原子整数

使用自定义的 AtomicData 实现之前线程安全的原子整数 Account 实现


import sun.misc.Unsafe;public class testThread24 {public static void main(String[] args) {Account.demo(new Account() {AtomicData atomicData = new AtomicData(10000);@Overridepublic Integer getBalance() {return atomicData.getData();}@Overridepublic void withdraw(Integer amount) {atomicData.decrease(amount);}});}
}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;}
}

上面的代码引用到了下面的类

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;public class testThread19 {public static void main(String[] args) {Account account = new AccountSafe(10000);Account.demo(account);/*** 输出*  0 cost: 97 ms 线程安全*/}
}/*** AtomicInteger 原子整数 解决方案*/
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);}
}/*** synchronized 解决方案*/
class AccountUnsafe implements Account {//  Integer 整数private Integer balance;public AccountUnsafe(Integer balance) {this.balance = balance;}@Overridepublic synchronized Integer getBalance() {return balance;}@Overridepublic synchronized void withdraw(Integer amount) {balance -= amount;}
}interface Account {// 获取余额Integer getBalance();// 取款void withdraw(Integer amount);/*** 方法内会启动 1000 个线程,每个线程做 -10 元 的操作* 如果初始余额为 10000 那么正确的结果应当是 0*/static void demo(Account account) {List<Thread> 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");}
}

总结

在这里插入图片描述

http://www.khdw.cn/news/16056.html

相关文章:

  • 一个网站成本个人博客网页设计html
  • 网站建设服务标准武汉新一轮疫情
  • wordpress 好主题哦福州seo兼职
  • 苏州做网站的单位seo网络优化
  • 外贸网站怎么做外链电脑系统优化工具
  • 扁平化网站登录界面网站搜索工具
  • 网站图片上传功能怎么做的朋友圈广告投放平台
  • 建设网站建设哪家快磁力搜索引擎
  • 织梦网站怎么做伪静态参考消息网国内新闻
  • 可视化的做网站的app搜索引擎是指什么
  • 哪个网站做母婴用品好图片外链生成工具
  • 免费的行情软件网站下载免费网站优化技术
  • 景县有专业做网站人员吗什么是全网营销推广
  • wpf 网站开发seo专员是什么意思
  • 国产一级a做爰片免费网站交换友链是什么意思
  • 做网站购买空间多少钱百度信息流怎么投放
  • 红色色系网站百度销售
  • wordpress 建立第二个网站google登录
  • 青岛高端模板建站互联网广告优化
  • 多软件网站下载安装制作链接的小程序
  • 科技部做财务决算的网站是什么网店营销与推广策划方案
  • 山东网站建设公司关键词竞价排名名词解释
  • 长春电商网站建设多少钱百度app免费下载
  • 日照seo优化公司抖音搜索优化
  • 政务信息化建设网站网店交易平台
  • 电子商务网站建设与管理的有关论文百度热搜关键词排行榜
  • 成年人夜大seo查询 站长之家
  • 郑州做网站天强科技媒体网站
  • wordpress 底部 copyright 时间上海哪家优化公司好
  • 运营一个网站的成本外链发布平台有哪些