文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

基础篇:Java原子组件和同步组件

2024-12-03 13:16

关注

本文转载自微信公众号「潜行前行」,作者cscw 。转载本文请联系潜行前行公众号。  

 前言

在使用多线程并发编程的时,经常会遇到对共享变量修改操作。此时我们可以选择ConcurrentHashMap,ConcurrentLinkedQueue来进行安全地存储数据。但如果单单是涉及状态的修改,线程执行顺序问题,使用Atomic开头的原子组件或者ReentrantLock、CyclicBarrier之类的同步组件,会是更好的选择,下面将一一介绍它们的原理和用法

原子组件的实现原理CAS

应用场景

原子组件

基本类型原子类

  1. AtomicBoolean //布尔类型 
  2. AtomicInteger //正整型数类型 
  3. AtomicLong   //长整型类型 

使用示例

  1. public static void main(String[] args) throws Exception { 
  2.     AtomicBoolean atomicBoolean = new AtomicBoolean(false); 
  3.     //异步线程修改atomicBoolean 
  4.     CompletableFuture future = CompletableFuture.runAsync(() ->{ 
  5.         try { 
  6.             Thread.sleep(1000); //保证异步线程是在主线程之后修改atomicBoolean为false 
  7.             atomicBoolean.set(false); 
  8.         }catch (Exception e){ 
  9.             throw new RuntimeException(e); 
  10.         } 
  11.     }); 
  12.     atomicBoolean.set(true); 
  13.     future.join(); 
  14.     System.out.println("boolean value is:"+atomicBoolean.get()); 
  15. ---------------输出结果------------------ 
  16. boolean value is:false 

引用类原子类

  1. AtomicReference 
  2. //加时间戳版本的引用类原子类 
  3. AtomicStampedReference 
  4. //相当于AtomicStampedReference,AtomicMarkableReference关心的是 
  5. //变量是否还是原来变量,中间被修改过也无所谓 
  6. AtomicMarkableReference 
  1. public class AtomicReference implements java.io.Serializable { 
  2.     private static final long serialVersionUID = -1848883965231344442L; 
  3.     private static final VarHandle VALUE; 
  4.     static { 
  5.         try { 
  6.             MethodHandles.Lookup l = MethodHandles.lookup(); 
  7.             VALUE = l.findVarHandle(AtomicReference.class, "value", Object.class); 
  8.         } catch (ReflectiveOperationException e) { 
  9.             throw new ExceptionInInitializerError(e); 
  10.         } 
  11.     } 
  12.     private volatile V value; 
  13.     .... 

ABA问题

  1. public class AtomicStampedReference { 
  2.     private static class Pair { 
  3.         final T reference; 
  4.         final int stamp; 
  5.         private Pair(T reference, int stamp) { 
  6.             this.reference = reference; 
  7.             this.stamp = stamp; 
  8.         } 
  9.         static  Pair of(T reference, int stamp) { 
  10.             return new Pair(reference, stamp); 
  11.         } 
  12.     } 
  13.     private volatile Pair pair; 
  1. public class Main { 
  2.     public static void main(String[] args) throws Exception { 
  3.         Test old = new Test("hello"), newTest = new Test("world"); 
  4.         AtomicStampedReference reference = new AtomicStampedReference<>(old, 1); 
  5.         reference.compareAndSet(old, newTest,1,2); 
  6.         System.out.println("对象:"+reference.getReference().name+";版本号:"+reference.getStamp()); 
  7.     } 
  8. class Test{ 
  9.     Test(String name){ this.name = name; } 
  10.     public String name
  11. ---------------输出结果------------------ 
  12. 对象:world;版本号:2 

数组原子类

  1. AtomicIntegerArray //整型数组 
  2.  
  3. AtomicLongArray //长整型数组 
  4.  
  5. AtomicReferenceArray //引用类型数组 
  1. //元素默认初始化为0 
  2. AtomicIntegerArray array = new AtomicIntegerArray(2); 
  3. // 下标为0的元素,期待值是0,更新值是1 
  4. array.compareAndSet(0,0,1); 
  5. System.out.println(array.get(0)); 
  6. ---------------输出结果------------------ 

属性原子类

  1. AtomicIntegerFieldUpdater  
  2. AtomicLongFieldUpdater 
  3. AtomicReferenceFieldUpdater 
  1. public class Main { 
  2.     public static void main(String[] args) { 
  3.         AtomicReferenceFieldUpdater fieldUpdater = AtomicReferenceFieldUpdater.newUpdater(Test.class,String.class,"name"); 
  4.         Test test = new Test("hello world"); 
  5.         fieldUpdater.compareAndSet(test,"hello world","siting"); 
  6.         System.out.println(fieldUpdater.get(test)); 
  7.         System.out.println(test.name); 
  8.     } 
  9. class Test{ 
  10.     Test(String name){ this.name = name; } 
  11.     public volatile String name
  12. ---------------输出结果------------------ 
  13. siting 
  14. siting 

累加器

  1. Striped64 
  2. LongAccumulator 
  3. LongAdder 
  4. //accumulatorFunction:运算规则,identity:初始值 
  5. public LongAccumulator(LongBinaryOperator accumulatorFunction,long identity) 
  1. public static void main(String[] args) throws Exception { 
  2.     LongAccumulator accumulator = new LongAccumulator(Long::sum, 0); 
  3.     for(int i=0;i<100000;i++){ 
  4.         CompletableFuture.runAsync(() -> accumulator.accumulate(1)); 
  5.     } 
  6.     Thread.sleep(1000); //等待全部CompletableFuture线程执行完成,再获取 
  7.     System.out.println(accumulator.get()); 
  8. ---------------输出结果------------------ 
  9. 100000 

同步组件的实现原理

java的多数同步组件会在内部维护一个状态值,和原子组件一样,修改状态值时一般也是通过cas来实现。而状态修改的维护工作被Doug Lea抽象出AbstractQueuedSynchronizer(AQS)来实现

AQS的原理可以看下之前写的一篇文章:详解锁原理,synchronized、volatile+cas底层实现[2]

同步组件

ReentrantLock、ReentrantReadWriteLock

  1. ReentrantLock lock = new ReentrantLock(); 
  2. if(lock.tryLock()){ 
  3.     //业务逻辑 
  4.     lock.unlock(); 
  1. public static void main(String[] args) throws Exception { 
  2.     ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); 
  3.     if(lock.readLock().tryLock()){ //读锁 
  4.         //业务逻辑 
  5.         lock.readLock().unlock(); 
  6.     } 
  7.     if(lock.writeLock().tryLock()){ //写锁 
  8.         //业务逻辑 
  9.         lock.writeLock().unlock(); 
  10.     } 

Semaphore实现原理和使用场景

  1. public static void main(String[] args) throws Exception { 
  2.     Semaphore semaphore = new Semaphore(2); 
  3.     for (int i = 0; i < 3; i++) 
  4.         CompletableFuture.runAsync(() -> { 
  5.             try { 
  6.                 System.out.println(Thread.currentThread().toString() + " start "); 
  7.                 if(semaphore.tryAcquire(1)){ 
  8.                     Thread.sleep(1000); 
  9.                     semaphore.release(1); 
  10.                     System.out.println(Thread.currentThread().toString() + " 无阻塞结束 "); 
  11.                 }else { 
  12.                     System.out.println(Thread.currentThread().toString() + " 被阻塞结束 "); 
  13.                 } 
  14.             } catch (Exception e) { 
  15.                 throw new RuntimeException(e); 
  16.             } 
  17.         }); 
  18.     //保证CompletableFuture 线程被执行,主线程再结束 
  19.     Thread.sleep(2000); 
  20. ---------------输出结果------------------ 
  21. Thread[ForkJoinPool.commonPool-worker-19,5,main] start  
  22. Thread[ForkJoinPool.commonPool-worker-5,5,main] start  
  23. Thread[ForkJoinPool.commonPool-worker-23,5,main] start  
  24. Thread[ForkJoinPool.commonPool-worker-23,5,main] 被阻塞结束  
  25. Thread[ForkJoinPool.commonPool-worker-5,5,main] 无阻塞结束  
  26. Thread[ForkJoinPool.commonPool-worker-19,5,main] 无阻塞结束  

可以看出三个线程,因为信号量设定为2,第三个线程是无法获取信息成功的,会打印阻塞结束

CountDownLatch实现原理和使用场景

  1. public static void main(String[] args) throws Exception { 
  2.     CountDownLatch count = new CountDownLatch(2); 
  3.     for (int i = 0; i < 2; i++) 
  4.         CompletableFuture.runAsync(() -> { 
  5.             try { 
  6.                 Thread.sleep(1000); 
  7.                 System.out.println(" CompletableFuture over "); 
  8.                 count.countDown(); 
  9.             } catch (Exception e) { 
  10.                 throw new RuntimeException(e); 
  11.             } 
  12.         }); 
  13.     //等待CompletableFuture线程的完成 
  14.     count.await(); 
  15.     System.out.println(" main over "); 
  16. ---------------输出结果------------------ 
  17.  CompletableFuture over  
  18.  CompletableFuture over  
  19.  main over  

CyclicBarrier实现原理和使用场景

  1. public static void main(String[] args) throws Exception { 
  2.     CyclicBarrier barrier = new CyclicBarrier(2); 
  3.     CompletableFuture.runAsync(()->{ 
  4.         try { 
  5.             System.out.println("CompletableFuture run start-"+ Clock.systemUTC().millis()); 
  6.             barrier.await(); //需要等待main线程也执行到await状态才能继续执行 
  7.             System.out.println("CompletableFuture run over-"+ Clock.systemUTC().millis()); 
  8.         }catch (Exception e){ 
  9.             throw new RuntimeException(e); 
  10.         } 
  11.     }); 
  12.     Thread.sleep(1000); 
  13.     //和CompletableFuture线程相互等待 
  14.     barrier.await(); 
  15.     System.out.println("main run over!"); 
  16. ---------------输出结果------------------ 
  17. CompletableFuture run start-1609822588881 
  18. main run over! 
  19. CompletableFuture run over-1609822589880 

StampedLock

  1. //获取读锁,自旋获取,返回一个戳值 
  2. public long readLock() 
  3. //尝试加读锁,不成功返回0 
  4. public long tryReadLock() 
  5. //解锁 
  6. public void unlockRead(long stamp)  
  7. //获取写锁,自旋获取,返回一个戳值 
  8. public long writeLock() 
  9. //尝试加写锁,不成功返回0 
  10. public long tryWriteLock() 
  11. //解锁 
  12. public void unlockWrite(long stamp) 
  13. //尝试乐观读读取一个时间戳,并配合validate方法校验时间戳的有效性 
  14. public long tryOptimisticRead() 
  15. //验证stamp是否有效 
  16. public boolean validate(long stamp) 
  1. public static void main(String[] args) throws Exception { 
  2.     StampedLock stampedLock = new StampedLock(); 
  3.     long stamp = stampedLock.tryOptimisticRead(); 
  4.     //判断版本号是否生效 
  5.     if (!stampedLock.validate(stamp)) { 
  6.         //获取读锁,会空转 
  7.         stamp = stampedLock.readLock(); 
  8.         long writeStamp = stampedLock.tryConvertToWriteLock(stamp); 
  9.         if (writeStamp != 0) { //成功转为写锁 
  10.             //fixme 业务操作 
  11.             stampedLock.unlockWrite(writeStamp); 
  12.         } else { 
  13.             stampedLock.unlockRead(stamp); 
  14.             //尝试获取写读 
  15.             stamp = stampedLock.tryWriteLock(); 
  16.             if (stamp != 0) { 
  17.                 //fixme 业务操作 
  18.                 stampedLock.unlockWrite(writeStamp); 
  19.             } 
  20.         } 
  21.     } 
  22. }     

参考文章

参考资料

[1]详解锁原理,synchronized、volatile+cas底层实现: https://juejin.cn/post/6854573210768900110

[2]详解锁原理,synchronized、volatile+cas底层实现: https://juejin.cn/post/6854573210768900110

[3]并发之Striped64(l累加器): https://www.cnblogs.com/gosaint/p/9129867.html

 

来源:潜行前行内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯