基础篇:JAVA原子组件和同步组件 (2)

使用示例

//元素默认初始化为0 AtomicIntegerArray array = new AtomicIntegerArray(2); // 下标为0的元素,期待值是0,更新值是1 array.compareAndSet(0,0,1); System.out.println(array.get(0)); ---------------输出结果------------------ 1 属性原子更新类 AtomicIntegerFieldUpdater  AtomicLongFieldUpdater AtomicReferenceFieldUpdater

如果操作对象是某一类型的属性,可以使用AtomicIntegerFieldUpdater原子更新,不过类的属性需要定义成volatile修饰的变量,保证该属性在各个线程的可见性,否则会报错

使用示例

public class Main { public static void main(String[] args) { AtomicReferenceFieldUpdater<Test,String> fieldUpdater = AtomicReferenceFieldUpdater.newUpdater(Test.class,String.class,"name"); Test test = new Test("hello world"); fieldUpdater.compareAndSet(test,"hello world","siting"); System.out.println(fieldUpdater.get(test)); System.out.println(test.name); } } class Test{ Test(String name){ this.name = name; } public volatile String name; } ---------------输出结果------------------ siting siting 累加器 Striped64 LongAccumulator LongAdder //accumulatorFunction:运算规则,identity:初始值 public LongAccumulator(LongBinaryOperator accumulatorFunction,long identity)

LongAccumulator和LongAdder都继承于Striped64,Striped64的主要思想是和ConcurrentHashMap有点类似,分段计算,单个变量计算并发性能慢时,我们可以把数学运算分散在多个变量,而需要计算总值时,再一一累加起来

LongAdder相当于LongAccumulator一个特例实现

LongAccumulator的示例

public static void main(String[] args) throws Exception { LongAccumulator accumulator = new LongAccumulator(Long::sum, 0); for(int i=0;i<100000;i++){ CompletableFuture.runAsync(() -> accumulator.accumulate(1)); } Thread.sleep(1000); //等待全部CompletableFuture线程执行完成,再获取 System.out.println(accumulator.get()); } ---------------输出结果------------------ 100000 同步组件的实现原理

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

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

同步组件 ReentrantLock、ReentrantReadWriteLock

ReentrantLock、ReentrantReadWriteLock都是基于AQS(AbstractQueuedSynchronizer)实现的。因为它们有公平锁和非公平锁的区分,因此没直接继承AQS,而是使用内部类去继承,公平锁和非公平锁各自实现AQS,ReentrantLock、ReentrantReadWriteLock再借助内部类来实现同步

ReentrantLock的使用示例

ReentrantLock lock = new ReentrantLock(); if(lock.tryLock()){ //业务逻辑 lock.unlock(); }

ReentrantReadWriteLock的使用示例

public static void main(String[] args) throws Exception { ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); if(lock.readLock().tryLock()){ //读锁 //业务逻辑 lock.readLock().unlock(); } if(lock.writeLock().tryLock()){ //写锁 //业务逻辑 lock.writeLock().unlock(); } } Semaphore实现原理和使用场景

Semaphore和ReentrantLock一样,也有公平和非公平竞争锁的策略,一样也是通过内部类继承AQS来实现同步

通俗解释:假设有一口井,最多有三个人的位置打水。每有一个人打水,则需要占用一个位置。当三个位置全部占满时,第四个人需要打水,则要等待前三个人中一个离开打水位,才能继续获取打水的位置

使用示例

public static void main(String[] args) throws Exception { Semaphore semaphore = new Semaphore(2); for (int i = 0; i < 3; i++) CompletableFuture.runAsync(() -> { try { System.out.println(Thread.currentThread().toString() + " start "); if(semaphore.tryAcquire(1)){ Thread.sleep(1000); semaphore.release(1); System.out.println(Thread.currentThread().toString() + " 无阻塞结束 "); }else { System.out.println(Thread.currentThread().toString() + " 被阻塞结束 "); } } catch (Exception e) { throw new RuntimeException(e); } }); //保证CompletableFuture 线程被执行,主线程再结束 Thread.sleep(2000); } ---------------输出结果------------------ Thread[ForkJoinPool.commonPool-worker-19,5,main] start Thread[ForkJoinPool.commonPool-worker-5,5,main] start Thread[ForkJoinPool.commonPool-worker-23,5,main] start Thread[ForkJoinPool.commonPool-worker-23,5,main] 被阻塞结束 Thread[ForkJoinPool.commonPool-worker-5,5,main] 无阻塞结束 Thread[ForkJoinPool.commonPool-worker-19,5,main] 无阻塞结束

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

CountDownLatch实现原理和使用场景

CountDownLatch也是靠AQS实现的同步操作

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpwgps.html