并发工具类
AQS
java.util.concurrent.locks.AbstractQueuedSynchronizer
java.util.concurrent.locks包基于AQS实现
1、抽象队列同步器简称AQS,它同步器的基础组件,JUC种锁的底层实现均依赖于AQS,开发不需要使用(了解原理和构成)。。
2、采用FIFO的双向队列实现,队列元素为Node(静态内部类),Node内的thread变量用于存储进入队列的线程。
3、Node节点内部的SHARED用来标记该线程是获取共享资源时被阻塞挂起后放入AQS队列的,
EXCLUSIVE用来标记线程是获取独占资源时被挂起后放入AQS队列的。waitStatus记录当前线程等待状态,可以为CANCELLED(线程被取消了)、SIGNAL(线程需要被唤醒)、CONDITION(线程在条件队列里面等待)、PROPAGATE(释放共享资源时需要通知其他节点) ;prev记录当前节点的前驱节点,next记录当前节点的后继节点。
4、在AQS中维持了一个单一的状态信息state,可以通过getState、setState、compareAndSetState函数修改其值。对于ReentrantLock的实现来说,state可以用来表示当前线程获取锁的可重入次数;对于读写锁ReentrantReadWriteLock来说,state的高16位表示读状态,也就是获取该读锁的次数,低16位表示获取到写锁的线程的可重入次数;对于semaphore来说,state用来表示当前可用信号的个数;对于
CountDownlatch来说,state用来表示计数器当前的值。
源码
有两个内部类Node和ConditionObject
Node源码
static final class Node {
/** Marker to indicate a node is waiting in shared mode */
//共享挂起
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode */
//排他挂起
static final Node EXCLUSIVE = null;
/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3;
/**
* Status field, taking on only the values:
* SIGNAL: The successor of this node is (or will soon be)
* blocked (via park), so the current node must
* unpark its successor when it releases or
* cancels. To avoid races, acquire methods must
* first indicate they need a signal,
* then retry the atomic acquire, and then,
* on failure, block.
* CANCELLED: This node is cancelled due to timeout or interrupt.
* Nodes never leave this state. In particular,
* a thread with cancelled node never again blocks.
* CONDITION: This node is currently on a condition queue.
* It will not be used as a sync queue node
* until transferred, at which time the status
* will be set to 0. (Use of this value here has
* nothing to do with the other uses of the
* field, but simplifies mechanics.)
* PROPAGATE: A releaseShared should be propagated to other
* nodes. This is set (for head node only) in
* doReleaseShared to ensure propagation
* continues, even if other operations have
* since intervened.
* 0: None of the above
*
* The values are arranged numerically to simplify use.
* Non-negative values mean that a node doesn't need to
* signal. So, most code doesn't need to check for particular
* values, just for sign.
*
* The field is initialized to 0 for normal sync nodes, and
* CONDITION for condition nodes. It is modified using CAS
* (or when possible, unconditional volatile writes).
*/
volatile int waitStatus;
/**
* Link to predecessor node that current node/thread relies on
* for checking waitStatus. Assigned during enqueuing, and nulled
* out (for sake of GC) only upon dequeuing. Also, upon
* cancellation of a predecessor, we short-circuit while
* finding a non-cancelled one, which will always exist
* because the head node is never cancelled: A node becomes
* head only as a result of successful acquire. A
* cancelled thread never succeeds in acquiring, and a thread only
* cancels itself, not any other node.
*/
volatile Node prev;
/**
* Link to the successor node that the current node/thread
* unparks upon release. Assigned during enqueuing, adjusted
* when bypassing cancelled predecessors, and nulled out (for
* sake of GC) when dequeued. The enq operation does not
* assign next field of a predecessor until after attachment,
* so seeing a null next field does not necessarily mean that
* node is at end of queue. However, if a next field appears
* to be null, we can scan prev's from the tail to
* double-check. The next field of cancelled nodes is set to
* point to the node itself instead of null, to make life
* easier for isOnSyncQueue.
*/
volatile Node next;
/**
* The thread that enqueued this node. Initialized on
* construction and nulled out after use.
*/
//存放线程
volatile Thread thread;
AQS中的变量
/**
* Head of the wait queue, lazily initialized. Except for
* initialization, it is modified only via method setHead. Note:
* If head exists, its waitStatus is guaranteed not to be
* CANCELLED.
*/
private transient volatile Node head;
/**
* Tail of the wait queue, lazily initialized. Modified only via
* method enq to add new wait node.
*/
private transient volatile Node tail;
/**
* The synchronization state.
*/
private volatile int state;
类图
ReentranLock原理
对于ReentranLock
构造相当于构造了AQS,第一次进入时通过unsafe类修改可重入次数state(从AQS记录下来),并记录当前持有排他锁的进程,后续再获取锁的时候,判断是否是第一次获取锁,判断获取锁的进程和当前持有排他锁的是否是同一个线程,然后再修改state状态,注意可重入次数是有限的,就是state这个变量的最大值
package com.mkevin.demo8;
import java.util.concurrent.locks.ReentrantLock;
/**
* 通过跟踪ReentrantLock的运行, 理解AQS的原理和作用
*/
public class AQSDemo0 {
public static void main(String[] args) throws InterruptedException {
ReentrantLock rl = new ReentrantLock();
System.out.println(Thread.currentThread().getName()+" start rl.lock");
rl.lock();
rl.lock();
Thread th = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" start rl.lock");
System.out.println("start re lock2");
rl.lock();
}
});
th.start();
th.join();
rl.unlock();
System.out.println(Thread.currentThread().getName()+" start rl.unlock");
System.out.println("main is over");
}
}
打断点
查看成员,ReentranLock
所依赖的同步类以及公平锁和非公平锁所依赖的Fair和Nofair都是基于AQS实现的
执行完构造方法,构造完AQS
调用lock,强制进入断点
继续进入,调用的是NonfairSync的lock方法,想要将变量从0设置成1
继续进入,可以看出向设置的是stateOffset位置对应的变量
stateOffset是state字段的偏移量
在AQS中维持了一个单一的状态信息state,可以通过getState、setState、compareAndSetState函数修改其值。对于ReentrantLock的实现来说,state可以用来表示当前线程获取锁的可重入次数;对于读写锁ReentrantReadWriteLock来说,state的高16位表示读状态,也就是获取该读锁的次数,低16位表示获取到写锁的线程的可重入次数;对于semaphore来说,state用来表示当前可用信号的个数;对于
CountDownlatch来说,state用来表示计数器当前的值。
将state变量从0设置成1后,调用setExclusiveOwnerThread
方法,这个类是AQS的父类,这个方法记录了是谁持有了排他锁(本例中是main线程)
AbstractOwnableSynchronizer
只有一个变量,记录谁持有了排他锁
这时再看ReentrantLock
的状态
第二次获取锁再进入,这时候再将state从0设置成1就会失败,程序走的是acqiure
进入执行acquire
进入执行tryAcquire
,调用的是非公平锁
进入nonfairTryAcquire
方法
ReentrantLock
状态改变
可以看出,可重入次数state是有限的,跟这个变量的容量有关
继续执行,th线程启动
切换到其他线程,执行过程:设置state失败,执行acquire,获取锁失败(和持有排他锁的不是同一个线程),tryAcquire
获取锁失败,加入等待队列,参数可以看出是加入一个排他锁线程,进入addWaiter
方法,将线程加入队列, 进入acquireQueued
方法,死循环中尝试获取锁失败,shouldParkAfterFailedAcquire
前驱节点的状态为SIGNAL唤醒,然后执行park方法将当前的线程阻塞
锁重入的状态是state=2
强制进入方法,执行到更改state
设置失败执行acquire方法,继续执行下去尝试获得锁
tryAcquire
获取锁失败,加入等待队列,参数可以看出是加入一个排他锁线程
进入addWaiter
方法,将线程加入队列,(如果队列为空)设置一个前驱空节点,该方法就是循环调用CAS,即使有高并发的场景,无限循环将会最终成功把当前线程追加到队尾(或设置队头)
继续执行,thread-0线程进入队列
返回后进入acquireQueued
方法,进入一个死循环
进入predecessor()
方法
再次tryAcquire
尝试获取锁失败
继续执行,判断获取失败之后是否需要park
进入shouldParkAfterFailedAcquire
,设置前驱节点的状态为需要通知Node.SIGNAL
返回false,继续acquireQueued
方法死循环,又执行前一次循环相同的步骤,执行到判断是否需要阻塞shouldParkAfterFailedAcquire
,由于前一次设置状态为SIGNAL,所以返回true
判断shouldParkAfterFailedAcquire
为true,继续执行下一个判断
继续执行,调用了LockSupport
,它的的底层采用Unsafe类来实现,是其他同步类的阻塞与唤醒的基础。park设置阻塞,(被unpark后)interrupted清除中断标记,这样就达到了前一个线程释放锁时,执行唤醒等待锁的线程的目的
程序继续执行到th.join();
阻塞,因为只释放了一个锁
unlock方法
调用了release方法
调用了tryRelease
方法
回到实现类的方法,getState是2,release是1(见unlock函数传入的参数),state不为0,执行setState更新,返回false
需要再调用一次unlock,c=0(没有线程占有排他锁),则实行setExclusiveOwnerThread(null);`把持有排他锁的线程设置为null(图中注释有误,节点不为空,节点里的线程为空)
进入unparkSuccessor方法,这段代码的意思在于从队尾向前找出第一个可以unpark的线程,
在阻塞的地方重新执行死循环
执行tryAcquire
设置当前线程为头节点
AQS.ConditionObject
package com.mkevin.demo8;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* 观察Condition的运行机制
*/
public class AQSDemo1 {
static ReentrantLock rl = new ReentrantLock();
static Condition condition = rl.newCondition();
public static void main(String[] args) throws InterruptedException {
new Runner().start();
new Runner().start();
new Runner().start();
}
static class Runner extends Thread{
@Override
public void run() {
try {
Thread.sleep(1000);
rl.lock();
System.out.println(Thread.currentThread().getName()+" before await..");
//condition.signal();
condition.await();
System.out.println(Thread.currentThread().getName()+" after await..");
rl.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
打上断点
强制进入newCondition
继续进入sync.newCondition
进入ConditionObject
执行的是AQS的构造器(所以本质是借助AQS的内部类实现的)
继续执行程序
启动三个线程,没法停住,进入await方法打断点没有效果,重启只在await处打上断点,并进入
进入addConditionWaiter方法
设置完后查看状态
放入的都是线程1
继续执行,进入park
后续的线程会执行到nextWaiter
添加进入了队列
从这里可以看出ReentrantLock.newCondition()创建的每一个Condition对象,实质上都是AQS.ConditionObject对象,而这个对象也是一个FIFO的队列
查看ConditionObject源码
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;
/**
* Creates a new {@code ConditionObject} instance.
*/
public ConditionObject() { }
// Internal methods
/**
* Adds a new waiter to wait queue.
* @return its new wait node
*/
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
/**
* Removes and transfers nodes until hit non-cancelled one or
* null. Split out from signal in part to encourage compilers
* to inline the case of no waiters.
* @param first (non-null) the first node on condition queue
*/
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
//省略...
}
查看doSignal方法中的transferForSignal方法
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
//修改状态
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
//接入队列,返回前节点
Node p = enq(node);
int ws = p.waitStatus;
//将前节点的状态改为SIGNAL
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
锁的分类
乐观锁/悲观锁:是否在修改之前给记录增加排它锁
公平锁/非公平锁:请求锁的时间顺序是否与获得锁的时间顺序—致。一致为公平锁,不一致为非公平锁
共享锁/独占锁:是否可以被多个线程共同持有,可以则为共享锁、不可以则为独占锁
可重入锁:一个线程再次获取它自己已经获取的锁时是否会被阻塞
自旋锁:无法获取锁时是否立刻阻塞,还是继续尝试获取指定次数
ReentrantLock:可重入锁、独占锁、悲观锁
乐观锁/悲观锁
不加锁,更新数据是对比数据的版本
1.乐观锁和悲观锁的概念来自于数据库
2.悲观锁对数据被修改持悲观态度,认为数据很容易就会被其他线程修改,所以在处理数据之前先加锁,处理完毕释放锁。
3.乐观锁对数据被修改持乐观态度,认为数据一般情况下不会被其他线程修改,所以在处理数据之前不会加锁,而是在数据进行更新时进行冲突检测。
4.对于数据库的悲观锁就是排它锁,在处理数据之前,先尝试给记录加排它锁,如果成功则继续处理,如果失败则挂起或抛出异常,直到数据处理完毕释放锁。
5.对于数据库的乐观锁所典型的就是CAS方式更新,例如:update name=kevin’where id=1 and name='kevinO’,在更新数据的时候校验这个值是否发生了变化,类似于CAS的操作。
公平锁/非公平锁
1.据线程获取锁的抢占机制,锁可以分为公平锁和非公平锁,最早请求锁的线程将最早获取到锁。而非公平锁则先请求不一定先得。JUC中的ReentrantLock提供了公平和非公平锁特性。·
2.公平锁:ReentrantLock pairLock = new ReentrantLock(true)
3.非公平锁:ReentrantLock pairLock = new ReentrantLock(false),如果构造函数不传递参数,则默认是非公平锁
4.非必要情况下使用非公平锁,公平锁存在性能开销
独占锁和共享锁
1.只能被单个线程所持有的锁是独占锁,可以被多个线程持有的锁是共享锁。
2. ReentrantLock就是以独占方式实现的,属于悲观锁
3. ReadWriteLock读锁是以共享锁方式实现的,属于乐观锁;写锁都是独占锁
4. StampedLock的写锁、悲观读锁,属于悲观锁。
5. StampedLock的乐观读锁,悲观读锁,属于乐观锁。
可重入锁
1.当一个线程想要获取本线程已经持有的锁时,不会被阻塞,而是能够再次获得这个锁,这就
是重入锁。
2. Synchornized是一种可重入锁,内部维护-
-个线程标志(谁持有锁),以及一个计数器。
3. ReentrantLock也是一种可重入锁
4.ReadWriteLock、StampedLock的读锁也是可重入锁
自旋锁
1.当获取锁的时候如果发现锁已经被其他线程占有,则不阻塞自己,也不释放CPU使用权,而是尝试多次获取,如果尝试了指定次数之后仍然没有获得锁,再阻塞线程。
2.自旋锁认为锁不会被长时间持有,使用CPU时间来换取线程上下文切换的开销,从而提高性能。但是可能会浪费CPU资源。
3.-XX:PreBlockSpin=n可以设置自旋次数(已经成为了历史),在Jdk7u40时被删除了,其实在jkd6的时候就已经无效了,现在HotSpotVM采用的是adaptive spinning(自适应自旋),虚拟机会根据情况来对每个线程使用不同的自旋次数。
CAS代码中用到的死循环获取某些东西就是自旋行为
高并发随机数ThreadLocalRandom与Random分析
Random存在性能缺陷,主要原因是要不断的计算新的种子更新原种子,使用CAS方法。高并发的情况下会造成大量的线程自旋,而只有一个线程会更新成功。
ThreadLocalRandom采用ThreadLocal的机制,每一个线程都是用自己的种子去进行计算下一个种子,规避CAS在并发下的问题。
查看Random代码
public int nextInt(int bound) {
if (bound <= 0)
throw new IllegalArgumentException(BadBound);
int r = next(31);
int m = bound - 1;
if ((bound & m) == 0) // i.e., bound is a power of 2
r = (int)((bound * (long)r) >> 31);
else {
for (int u = r;
u - (r = u % bound) + m < 0;
u = next(31))
;
}
return r;
}
查看next方法
/**
* Generates the next pseudorandom number. Subclasses should
* override this, as this is used by all other methods.
*
* <p>The general contract of {@code next} is that it returns an
* {@code int} value and if the argument {@code bits} is between
* {@code 1} and {@code 32} (inclusive), then that many low-order
* bits of the returned value will be (approximately) independently
* chosen bit values, each of which is (approximately) equally
* likely to be {@code 0} or {@code 1}. The method {@code next} is
* implemented by class {@code Random} by atomically updating the seed to
* <pre>{@code (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)}</pre>
* and returning
* <pre>{@code (int)(seed >>> (48 - bits))}.</pre>
*
* This is a linear congruential pseudorandom number generator, as
* defined by D. H. Lehmer and described by Donald E. Knuth in
* <i>The Art of Computer Programming,</i> Volume 3:
* <i>Seminumerical Algorithms</i>, section 3.2.1.
*
* @param bits random bits
* @return the next pseudorandom value from this random number
* generator's sequence
* @since 1.1
*/
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong seed = this.seed;
do {
//获取种子
oldseed = seed.get();
//计算新种子
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
通过CAS方法加循环的方式就是自旋
查看ThreadLocalRandom代码
/**
* Returns a pseudorandom {@code int} value between zero (inclusive)
* and the specified bound (exclusive).
*
* @param bound the upper bound (exclusive). Must be positive.
* @return a pseudorandom {@code int} value between zero
* (inclusive) and the bound (exclusive)
* @throws IllegalArgumentException if {@code bound} is not positive
*/
public int nextInt(int bound) {
if (bound <= 0)
throw new IllegalArgumentException(BadBound);
int r = mix32(nextSeed());
int m = bound - 1;
if ((bound & m) == 0) // power of two
r &= m;
else { // reject over-represented candidates
for (int u = r >>> 1;
u + m - (r = u % bound) < 0;
u = mix32(nextSeed()) >>> 1)
;
}
return r;
}
查看nextSeed方法
final long nextSeed() {
Thread t; long r; // read and update per-thread seed
UNSAFE.putLong(t = Thread.currentThread(), SEED,
//计算新种子
r = UNSAFE.getLong(t, SEED) + GAMMA);
return r;
}
查看SEED
这三个变量在Thread类中,创建新的线程时会分配这三个变量,各自线程计算种子都使用自己的种子
例子
random类使用
/**
* Random类使用,性能低
*/
public class RandomDemo0 {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
Random random = new Random();
CountDownLatch cd = new CountDownLatch(100);
CyclicBarrier barrier = new CyclicBarrier(100);
ExecutorService executor = Executors.newFixedThreadPool(100);
for(int i=0;i<100;i++){
executor.submit(new RandomDemo0Runner(barrier,"thread"+i,random,cd));
}
//所有线程都执行了cd.countDown()时,继续执行
cd.await();
long use = System.currentTimeMillis()-start;
System.out.println("main is over.."+use);
executor.shutdown();
}
}
class RandomDemo0Runner implements Runnable {
private CyclicBarrier barrier;
private String name;
//多个线程使用同一个random
private Random random;
private CountDownLatch cd;
public RandomDemo0Runner(CyclicBarrier barrier, String name,Random random, CountDownLatch cd) {
super();
this.barrier = barrier;
this.name = name;
this.random = random;
this.cd = cd;
}
@Override
public void run() {
try {
System.out.println(name + " 准备好了...");
//所有线程都到达这里才执行
barrier.await();
for(int j=0;j<10000;j++) {
//高并发CAS锁竞争
this.random.nextInt(50);
//System.out.println(Thread.currentThread().getName() + ">" + this.random.nextInt(50));
//提醒: 产生大量对象
//System.out.println(Thread.currentThread().getName() + ">" + new Random().nextInt(50));
}
cd.countDown();
} catch (InterruptedException e) {
System.out.println(name + " 中断异常!");
} catch (BrokenBarrierException e) {
System.out.println(name + " Barrier损坏异常!");
}
}
}
执行结果,使用时间90毫秒
thread98 准备好了...
thread99 准备好了...
main is over..90
ThreadLocalRandom类使用
/**
* ThreadLocalRandom 使用,性能高
*/
public class RandomDemo1 {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
CountDownLatch cd = new CountDownLatch(100);
CyclicBarrier barrier = new CyclicBarrier(100);
ExecutorService executor = Executors.newFixedThreadPool(100);
for(int i=0;i<100;i++){
executor.submit(new RandomDemo1Runner(barrier,"thread"+i,cd));
}
cd.await();
long use = System.currentTimeMillis()-start;
System.out.println("main is over.."+use);
executor.shutdown();
}
}
class RandomDemo1Runner implements Runnable {
private CyclicBarrier barrier;
private String name;
private CountDownLatch cd;
public RandomDemo1Runner(CyclicBarrier barrier, String name,CountDownLatch cd) {
super();
this.barrier = barrier;
this.name = name;
this.cd = cd;
}
@Override
public void run() {
try {
System.out.println(name + " 准备好了...");
barrier.await();
for(int j=0;j<10000;j++) {
ThreadLocalRandom.current().nextInt(50);
//System.out.println(Thread.currentThread().getName() + ">>" + ThreadLocalRandom.current().nextInt(50));
}
cd.countDown();
} catch (InterruptedException e) {
System.out.println(name + " 中断异常!");
} catch (BrokenBarrierException e) {
System.out.println(name + " Barrier损坏异常!");
}
}
}
执行结果
thread97 准备好了...
thread98 准备好了...
thread99 准备好了...
main is over..56
查看current方法
/**
* Returns the current thread's {@code ThreadLocalRandom}.
*
* @return the current thread's {@code ThreadLocalRandom}
*/
public static ThreadLocalRandom current() {
if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
localInit();
return instance;
}
查看localInit方法,发现没有初始化就会进行初始化变量
/**
* Initialize Thread fields for the current thread. Called only
* when Thread.threadLocalRandomProbe is zero, indicating that a
* thread local seed value needs to be generated. Note that even
* though the initialization is purely thread-local, we need to
* rely on (static) atomic generators to initialize the values.
*/
static final void localInit() {
int p = probeGenerator.addAndGet(PROBE_INCREMENT);
int probe = (p == 0) ? 1 : p; // skip 0
long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
Thread t = Thread.currentThread();
UNSAFE.putLong(t, SEED, seed);
UNSAFE.putInt(t, PROBE, probe);
}
高性能累加器
LongAdder、DoubleAdder、
LongAccumulatax、DoubleAccumulator
AtomicLong存在性能瓶颈,由于使用CAS方法。高并发的情况下会造成大量的线程自旋,而只有一个线程会更新成功,浪费CPU资源。LongAdder的思想是将单一的原子变量拆分为多个变量,从而分治再合并的思想)
查看Actomic源码
使用了CAS方法,更新value属性
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final long incrementAndGet() {
return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}
查看getAndAddLong,有自旋行为,直到更新才停止
public final long getAndAddLong(Object var1, long var2, long var4) {
long var6;
do {
var6 = this.getLongVolatile(var1, var2);
} while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
return var6;
}
valueOffset就是value
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicLong.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile long value;
LongAdder解决了高并发下自旋的问题
先进行判断并发压力,不大的情况下使用base就够了;线程增加争抢严重的时候,每个线程都去操作单独的变量cell;最后想要获取累加的结果时将所有的cell加上base得到。实际代码中cell是数组
查看LongAdder源码(理解原理)
/**
* Equivalent to {@code add(1)}.
*/
public void increment() {
add(1L);
}
/**
* Adds the given value.
*
* @param x the value to add
*/
public void add(long x) {
Cell[] as; long b, v; int m; Cell a;
//加1(x是1),如果没有设置成功,就要判断是否繁忙
if ((as = cells) != null || !casBase(b = base, b + x)) {
//是否是繁忙场景
boolean uncontended = true;
if (as == null || (m = as.length - 1) < 0 ||
(a = as[getProbe() & m]) == null ||
!(uncontended = a.cas(v = a.value, v + x)))
longAccumulate(x, null, uncontended);
}
}
查看longAccumulate方法
final void longAccumulate(long x, LongBinaryOperator fn,
boolean wasUncontended) {
int h;
if ((h = getProbe()) == 0) {
ThreadLocalRandom.current(); // force initialization
h = getProbe();
wasUncontended = true;
}
boolean collide = false; // True if last slot nonempty
for (;;) {
Cell[] as; Cell a; int n; long v;
if ((as = cells) != null && (n = as.length) > 0) {
if ((a = as[(n - 1) & h]) == null) {
if (cellsBusy == 0) { // Try to attach new Cell
Cell r = new Cell(x); // Optimistically create
//判断是否繁忙
if (cellsBusy == 0 && casCellsBusy()) {
boolean created = false;
try { // Recheck under lock
Cell[] rs; int m, j;
if ((rs = cells) != null &&
(m = rs.length) > 0 &&
rs[j = (m - 1) & h] == null) {
rs[j] = r;
created = true;
}
} finally {
cellsBusy = 0;
}
if (created)
break;
continue; // Slot is now non-empty
}
}
collide = false;
}
else if (!wasUncontended) // CAS already known to fail
wasUncontended = true; // Continue after rehash
else if (a.cas(v = a.value, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break;
else if (n >= NCPU || cells != as)
collide = false; // At max size or stale
else if (!collide)
collide = true;
else if (cellsBusy == 0 && casCellsBusy()) {
try {
if (cells == as) { // Expand table unless stale
Cell[] rs = new Cell[n << 1];
for (int i = 0; i < n; ++i)
//某一个cell值计算
rs[i] = as[i];
cells = rs;
}
} finally {
cellsBusy = 0;
}
collide = false;
continue; // Retry with expanded table
}
h = advanceProbe(h);
}
else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
boolean init = false;
try { // Initialize table
if (cells == as) {
Cell[] rs = new Cell[2];
rs[h & 1] = new Cell(x);
cells = rs;
init = true;
}
} finally {
cellsBusy = 0;
}
if (init)
break;
}
else if (casBase(v = base, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break; // Fall back on using base
}
}
查看LongAdder的sum方法
/**
* Returns the current sum. The returned value is <em>NOT</em> an
* atomic snapshot; invocation in the absence of concurrent
* updates returns an accurate result, but concurrent updates that
* occur while the sum is being calculated might not be
* incorporated.
*
* @return the sum
*/
//将base和cell数组中的值相加求和
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;
}
查看cell源码(父类Striped64中),cell中value的更新是采用CAS的方式进行更新
/**
* Padded variant of AtomicLong supporting only raw accesses plus CAS.
*
* JVM intrinsics note: It would be possible to use a release-only
* form of CAS here, if it were provided.
*/
@sun.misc.Contended static final class Cell {
volatile long value;
Cell(long x) { value = x; }
final boolean cas(long cmp, long val) {
return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
}
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long valueOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> ak = Cell.class;
valueOffset = UNSAFE.objectFieldOffset
(ak.getDeclaredField("value"));
} catch (Exception e) {
throw new Error(e);
}
}
}
查看Striped64的变量,通过对比CPU数和线程数判断是否繁忙
/** Number of CPUS, to place bound on table size */
static final int NCPU = Runtime.getRuntime().availableProcessors();
/**
* Table of cells. When non-null, size is a power of 2.
*/
transient volatile Cell[] cells;
/**
* Base value, used mainly when there is no contention, but also as
* a fallback during table initialization races. Updated via CAS.
*/
transient volatile long base;
/**
* Spinlock (locked via CAS) used when resizing and/or creating Cells.
*/
transient volatile int cellsBusy;
例子
做性能对比
使用原子类
package com.mkevin.demo5;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
public class LongAdderDemo0 {
public static void main(String[] args) throws InterruptedException {
CyclicBarrier barrier = new CyclicBarrier(100);
AtomicLong al = new AtomicLong();
List<Thread> list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
barrier.await();
for(int i=0;i<10000;i++) {
//做累加
al.incrementAndGet();
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
});
list.add(t);
}
long start = System.currentTimeMillis();
for (Thread th : list) {
th.start();
}
for (Thread th : list) {
//确保100个线程都执行完毕之后再向下执行
th.join();
}
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) + "ms, sum:" + al.get());
}
}
运行结果
耗时:32ms, sum:1000000
使用LongAdder
package com.mkevin.demo5;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
/**
* LongAddr 和 DoubleAddr的性能极高,采用cell的方式进行分化处理,比Atomic的性能更强
* <p>
* https://blog.csdn.net/fouy_yun/article/details/77825039
*/
public class LongAdderDemo1 {
public static void main(String[] args) throws InterruptedException {
CyclicBarrier barrier = new CyclicBarrier(100);
LongAdder longAdder = new LongAdder();
List<Thread> list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
barrier.await();
for(int i=0;i<10000;i++) {
longAdder.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
});
list.add(t);
}
long start = System.currentTimeMillis();
for (Thread th : list) {
th.start();
}
for (Thread th : list) {
th.join();
}
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) + "ms, sum:" + longAdder.sum());
}
}
运行结果
耗时:27ms, sum:1000000
Striped64是数字,继承自Number
累加器家族
进行多个操作的话要自行使用锁保证原子性,因为没有提供复杂的操作
没有incrementAndGet、decrementAndGet这种方法,只有单独的increment、longValue这种方法,如果组合使用则需要自己做同步控制,否则无法保证原子性。
LongAddr本质上是一种空间换时间的策略,累加器家族还有以下3种
java.util.concurrent.atomic.DoubleAdder
java.util.concurrent.atomic.LongAccumulator
java.util.concurrent.atomic.DoubleAccumulator(可自定义累加算法)
LongAdder是LongAccumulator的特例,DoubleAdder是DoubleAccumulator的特列,Accumulator的特点是可以设置初始值、自定义累加算法
LongAccumulator源码如下
public class LongAccumulator extends Striped64 implements Serializable {
private static final long serialVersionUID = 7249069246863182397L;
//自定义计算的接口
private final LongBinaryOperator function;
private final long identity;
/**
* Creates a new instance using the given accumulator function
* and identity element.
* @param accumulatorFunction a side-effect-free function of two arguments
* @param identity identity (initial value) for the accumulator function
*/
public LongAccumulator(LongBinaryOperator accumulatorFunction,
long identity) {
this.function = accumulatorFunction;
base = this.identity = identity;
}
/**
* Updates with the given value.
*
* @param x the value
*/
//执行计算
public void accumulate(long x) {
Cell[] as; long b, v, r; int m; Cell a;
if ((as = cells) != null ||
(r = function.applyAsLong(b = base, x)) != b && !casBase(b, r)) {
boolean uncontended = true;
if (as == null || (m = as.length - 1) < 0 ||
(a = as[getProbe() & m]) == null ||
!(uncontended =
(r = function.applyAsLong(v = a.value, x)) == v ||
a.cas(v, r)))
longAccumulate(x, function, uncontended);
}
}
//省略...
}
查看LongBinaryOperator源码
/**
* Represents an operation upon two {@code long}-valued operands and producing a
* {@code long}-valued result. This is the primitive type specialization of
* {@link BinaryOperator} for {@code long}.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #applyAsLong(long, long)}.
*
* @see BinaryOperator
* @see LongUnaryOperator
* @since 1.8
*/
@FunctionalInterface
public interface LongBinaryOperator {
/**
* Applies this operator to the given operands.
*
* @param left the first operand
* @param right the second operand
* @return the operator result
*/
long applyAsLong(long left, long right);
}
例子
package com.mkevin.demo5;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.function.LongBinaryOperator;
/**
* LongAccumulator是LongAdder的功能增强版。LongAdder的API只有对数值的加减,而LongAccumulator提供了自定义的函数操作
*
* https://blog.csdn.net/fouy_yun/article/details/77825039
*/
public class LongAccumulatorDemo1 {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
LongBinaryOperator lbn = new LongBinaryOperator() {
@Override
public long applyAsLong(long left, long right) {
return left+right*2+ ThreadLocalRandom.current().nextInt(10);
}
};
LongAccumulator la = new LongAccumulator(lbn,1);
System.out.println("初始值:"+la.longValue());
List<Thread> list = new ArrayList<>();
for(int i=0;i<1000;i++){
Thread t = new Thread(new Runnable() {
@Override
public void run() {
la.accumulate(2);
}
});
list.add(t);
}
for(Thread th : list){
th.start();
}
for(Thread th : list){
th.join();
}
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start)+",结果:"+la.longValue());
}
}
运行结果
初始值:1
耗时:89,结果:16570