packagecom.nike.erick.d05;importlombok.Getter;importjava.util.concurrent.TimeUnit;publicclassDemo01{publicstaticvoidmain(String[] args)throwsInterruptedException{BankService bankService =newBankService();for(int i =0; i <15; i++){newThread(()-> bankService.drawMoney()).start();}TimeUnit.SECONDS.sleep(3);System.out.println(bankService.getTotalMoney());}}classBankService{@Getterprivateint totalMoney =10;publicvoiddrawMoney(){if(totalMoney >0){try{/*模仿业务事件*/TimeUnit.MILLISECONDS.sleep(100);
totalMoney--;}catch(InterruptedException e){
e.printStackTrace();}}else{System.out.println("余额不足");return;}}}
2. 解决方案
2.1 悲观锁-synchronized
通过加锁,来实现线程间的互斥,实现线程安全的目的
2.2 乐观锁-CAS
不加锁实现共享资源的保护
Compare And Set
JDK提供了对应的CAS类来实现不加锁
AtomicInteger
private volatile int value;# 1. 获取最新值
public final int get();# 2. 比较,交换
public final boolean compareAndSet(int expectedValue, int newValue)
packagecom.nike.erick.d05;importlombok.Getter;importjava.util.concurrent.TimeUnit;importjava.util.concurrent.atomic.AtomicInteger;publicclassDemo02{publicstaticvoidmain(String[] args)throwsInterruptedException{Bank bank =newBank();for(int i =0; i <1000; i++){newThread(()-> bank.withDrawMoney()).start();}TimeUnit.SECONDS.sleep(5);System.out.println(bank.getTotalAmount().get());}}classBank{/*JDK提供的cas类*/@GetterprivateAtomicInteger totalAmount =newAtomicInteger(100);publicvoidwithDrawMoney(){while(true){/*最新值*/int before = totalAmount.get();if(before<=0){break;}/*想修改的值*/int next = before -1;boolean isChanged = totalAmount.compareAndSet(before, next);if(isChanged){break;}}}}
packagecom.erick.multithread.d4;importjava.util.concurrent.atomic.AtomicInteger;importjava.util.function.IntUnaryOperator;publicclassDemo06{}classAccount{// 无参构造为0privateAtomicInteger account =newAtomicInteger(3);publicvoidmethod01(){/*自增并返回最新结果*/int result = account.incrementAndGet();System.out.println(result);}publicvoidmethod02(){/*获取最新结果并自增*/int result = account.getAndDecrement();System.out.println(result);}publicvoidmethod03(){/*自减并返回最新结果*/int result = account.decrementAndGet();System.out.println(result);}publicvoidmethod04(){/*返回最新结果并自减*/int result = account.getAndDecrement();System.out.println(result);}publicvoidmethod05(){/*先处理并返回结果: 函数接口: IntUnaryOperator*/int result = account.updateAndGet(newIntUnaryOperator(){@OverridepublicintapplyAsInt(int operand){return operand *10;}});System.out.println(result);}publicvoidmethod06(){/*返回结果并更新*/int result = account.getAndUpdate(newIntUnaryOperator(){@OverridepublicintapplyAsInt(int operand){return operand *10;}});System.out.println(result);}publicvoidmethod07(){/*如果需要减,则传递负数即可*/int result = account.addAndGet(5);System.out.println(result);}publicvoidmethod08(){int result = account.getAndAdd(5);System.out.println(result);}}
1.2 CAS代码简化
packagecom.erick.multithread.d4;importlombok.Getter;importjava.util.concurrent.TimeUnit;importjava.util.concurrent.atomic.AtomicInteger;publicclassDemo07{privatestaticAccountService accountService =newAccountService();publicstaticvoidmain(String[] args)throwsInterruptedException{for(int i =0; i <1000; i++){newThread(()-> accountService.withDraw()).start();}TimeUnit.SECONDS.sleep(5);System.out.println(accountService.getAccount().get());}}classAccountService{@GetterprivateAtomicInteger account =newAtomicInteger(100);publicvoidwithDraw(){if(account.get()<=0){return;}
account.decrementAndGet();}publicvoidwithDraw(int num){if(account.get()<=0){return;}
account.addAndGet(num);}}
1.3 CAS模拟
classBankService{@GetterprivateAtomicInteger leftMoney =newAtomicInteger(100);publicvoiddrawMoney(){sleepMills(1);detailWays(leftMoney,newIntUnaryOperator(){@OverridepublicintapplyAsInt(int operand){return operand -10;}});}privatevoiddetailWays(AtomicInteger value,IntUnaryOperator operator){while(true){int before = value.get();/**
* 1. IntUnaryOperator int applyAsInt(int operand);
* 2. 函数接口,自定义实现
*/int after = operator.applyAsInt(before);if(leftMoney.compareAndSet(before, after)){break;}}}privatevoidsleepMills(int mills){try{TimeUnit.MILLISECONDS.sleep(mills);}catch(InterruptedException e){
e.printStackTrace();}}}
2. 原子引用
一些其他计算场景,比如大数BigDecimal, 就要用到原子引用
用法和上面原子整数类似
2.1 AtomicReference
packagecom.erick.multithread.d4;importlombok.Getter;importjava.math.BigDecimal;importjava.util.concurrent.TimeUnit;importjava.util.concurrent.atomic.AtomicReference;publicclassDemo08{publicstaticvoidmain(String[] args)throwsInterruptedException{AccountService01 accountService =newAccountService01();for(int i =0; i <1000; i++){newThread(()-> accountService.withDraw(newBigDecimal("10"))).start();}TimeUnit.SECONDS.sleep(3);System.out.println(accountService.getBalance().get());}}classAccountService01{@GetterprivateAtomicReference<BigDecimal> balance =newAtomicReference<>(newBigDecimal("1000"));publicvoidwithDraw(BigDecimal count){while(true){if(balance.get().compareTo(newBigDecimal("0"))<=0){break;}BigDecimal previous = balance.get();BigDecimal next = previous.subtract(count);boolean isChanged = balance.compareAndSet(previous, next);if(isChanged){break;}}}}
2.2 AtomicStampedReference
ABA场景
线程-1,在CAS的过程中,线程2将原来的值从A改到B,然后又改回到A
线程-1的CAS交换会成功,但是对比的值,其实已经被改过
一般情况下,ABA并不会影响具体的业务
packagecom.erick.multithread.d4;importjava.util.concurrent.TimeUnit;importjava.util.concurrent.atomic.AtomicReference;publicclassDemo09{privatestaticAtomicReference<String> result =newAtomicReference<>("A");publicstaticvoidmain(String[] args)throwsInterruptedException{String previous = result.get();String next ="C";// 其他线程将该值从A->B 再从B->Amethod();TimeUnit.SECONDS.sleep(4);boolean isChanged = result.compareAndSet(previous, next);System.out.println("isChanged: "+ isChanged +" result: "+ result.get());}privatestaticvoidmethod()throwsInterruptedException{newThread(()->{while(true){String previous = result.get();String next ="B";if(result.compareAndSet(previous, next)){System.out.println("A->B交换成功");break;}}}).start();TimeUnit.SECONDS.sleep(1);newThread(()->{while(true){String previous = result.get();String next ="A";if(result.compareAndSet(previous, next)){System.out.println("B->A交换成功");break;}}}).start();}}
packagecom.erick.multithread.d5;importjava.util.concurrent.TimeUnit;importjava.util.concurrent.atomic.AtomicLong;importjava.util.concurrent.atomic.LongAdder;publicclassDemo06{privatestaticAtomicLong first =newAtomicLong(0);privatestaticLongAdder second =newLongAdder();publicstaticvoidmain(String[] args)throwsInterruptedException{method01();method02();}privatestaticvoidmethod01()throwsInterruptedException{/*p循环主要目的是JIT编译器优化*/for(int p =0; p <4; p++){long start =System.currentTimeMillis();for(int i =0; i <100; i++){newThread(()->{for(int j =0; j <100000; j++){
first.incrementAndGet();}}).start();}TimeUnit.SECONDS.sleep(2);System.out.println("AtomicLong:"+(System.currentTimeMillis()- start));}}privatestaticvoidmethod02()throwsInterruptedException{for(int p =0; p <4; p++){long start =System.currentTimeMillis();for(int i =0; i <100; i++){newThread(()->{for(int j =0; j <100000; j++){
second.increment();}}).start();}TimeUnit.SECONDS.sleep(2);System.out.println("LongAdder:"+(System.currentTimeMillis()- start));}}}
前言
docker学习记录,内容参考
Docker Training Course for the Absolute Beginner Basic Command
docker pull <Image:只是下载image,不会运行docker run <Image>:启动image实例,如果image不在docker host上,docker会…
1. 状语从句九大类
时间状语从句:after / before / when / while / as / since / once / until地点状语从句:where原因状语从句:because / as / for / since让步状语从句:though / although / even if even though / while / as…