AQS(上) 同步队列AQS介绍篇

AQS——锁的底层支持

AbstractQueuedSynchronizer抽象同步队列简称AQS,它是实现同步器的基础组件,并发包中锁的底层就是使用AQS实现的。另外,大多数开发者可能永远不会直接使用AQS,但是知道其原理对于架构设计还是很有帮助的。下面看下AQS的类图结构,如图所示。

image-20200420175900001.png

由该图可以看到,AQS是一个FIFO的双向队列,其内部通过节点headtail记录队首和队尾元素,队列元素的类型为Node

阅读更多

LockSupport 工具类使用以及实现原理

JDK中的rt.jar包里面的是个LockSupport是个工具类,它的主要作用是挂起和唤醒线程,该工具类是创建锁和其他同步类的基础。

LockSupport类与每个使用它的线程都会关联一个许可证,在默认情况下调用LockSupport类的方法的线程是不持有许可证的。LockSupport是使用Unsafe类实现的,下面介绍LockSupport中的几个主要函数。

阅读更多

LongAccumulator类原理探究

LongAccumulator类原理探究

LongAdder类时LongAccumulator的一个特例,LongAccumulator比LongAdder的功能更强大。例如下面的构造函数,其中accumulatorFunction是一个双目运算器接口,其根据输入的两个参数返回一个计算值,identity则是LongAccumulator累加器的初始值。

1
2
3
4
5
6
7
8
9
10
11
public LongAccumulator(LongBinaryOperator accumulatorFunction,
long identity) {
this.function = accumulatorFunction;
base = this.identity = identity;
}

public interface LongBinaryOperator {

// 根据两个参数计算并返回一个值
long applyAsLong(long left, long right);
}
阅读更多

LongAdder(下)使用及代码分析

LongAdder代码分析

为了解决高并发下多线程对一个变量CAS争夺失败后进行自旋而造成的降低并发性能的问题,LongAdder在内部维护多个Cell元素(一个动态的Cell数组)来分担单个变量进行争夺开销。下面围绕以下话题从源码角度来分析LongAdder的实现!

  1. LongAdder的结构是怎样的?
  2. 当前线程应该访问Cell数组里面哪一个Cell元素?
  3. 如何初始化Cell数组?
  4. Cell数组如何扩容?
  5. 线程访问分配的Cell元素有冲突后如何处理?
  6. 如何保证线程操作被分配的Cell元素的原子性?
阅读更多

LongAdder(上)介绍篇

之前有篇文章讲过AtomicLong通过CAS提供了非阻塞的原子性操作,相比使用阻塞算法的同步器来说它的性能已经很好了,但是JDK开发组并不满足于此。使用AtomicLong时,在高并发下大量线程会同时去竞争更新同一个原子变量,但是由于同时只有一个线程的CAS操作会成功,这就造成了大量线程竞争失败后,会通过无限循环不断进行自旋尝试CAS操作,而这会白白浪费CPU资源。

因此JDK8新增了一个原子性递增或者递减类LongAdder用来克服在高并发下使用AtomicLong的缺点。既然AtomicLong的性能瓶颈是由于多线程同时去竞争一个变量的更新而产生的,那么如果把一个变量分解为多个变量,让同样多的线程去竞争多个资源,是不是就解决了性能问题?是的,LongAdder就是这个思路。下面通过一张的图来理解两者设计的不同之处。

image-20200411011831604.png

阅读更多

CountDownLatch介绍及使用

CountDownLatch作用阻塞一个或多个线程等待其他线程完成操作。

定义初始化的时候,需要传入一个正数来初始化计数器(0也可以,但这样定义没有实际意义)。有两个方法countDown()用于递减计数器,await()方法阻塞当前线程,直到计数器递减为0

CountDownLatch通常用于多个线程之间的协调工作。

假设有如下情节:

同时获取5张表的数据并一同返回

为了让cpu更好的得到利用,程序执行效率更高,使用多线程来完成。

看如下代码:

阅读更多

Java并发-读锁/写锁

这篇看一下JUC包提供的读写锁(共享锁/独占锁)。

之前我们都知道在一个变量被读或者写数据的时候每次只有一个线程可以执行,那么今天我们来看一下读写锁,读写两不误ReadWriteLock

这里有两个概念:

独占锁:

指该锁一次只能被一个线程所持有。(ReentrantLock和Synchronized都属于独占锁)。

共享锁:

指该锁可被多个线程所持有。

ReentrantReadWriteLock其读锁是共享锁,共写锁是独占锁。

读锁的共享锁可以保证并发读是非常高效的,读写,写读,写写的过程是互斥的。

直接使用ReentrantReadWriteLock写段代码看一下:

阅读更多

Atomic包中原子类的使用,以及内部CAS的实现和原理

上篇文章有说过 多线程环境下 进行变量属性 自增操作时会造成线程不安全的情况,也有说到 volatile **关键字,最后也不能保证线程安全,因为多线程情况下 他不能保证原子性,不能保证写操作过程不可以被插队,最后有提到java.util.current.atomic包中的AtomicInteger**类,那么它是如何实现线程安全的呢?,让我们一探究竟!

image

阅读更多

Volatile关键字介绍及使用

volatile关键字java虚拟机提供的轻量级同步机制

  • 保证可见性

  • 禁止指令重排序

  • 不保证原子性

可见性:

什么叫可见性呢,首先要说一下JAVA虚拟机内存,JAVA虚拟机内存模型规定,所有的变量都放在计算机的主内存当中,

阅读更多