JDK8 LongAdder源码浅析

	/**
	 * Base value, used mainly when there is no contention, but also as a
	 * fallback during table initialization races. Updated via CAS.
	 */
	volatile long base;
	
	/**
	 * Table of cells. When non-null, size is a power of 2.
	 */
	volatile Cell[] cells; // 可以理解为选举活动中的投票箱。如果只有一个箱子,那大家就要排队,队伍可能很长。但如果有多个箱,有多个队伍,分开计票,最后汇总,就能提高效率
	
	/** Number of CPUS, to place bound on table size */
	static final int NCPU = Runtime.getRuntime().availableProcessors();
	
	/**
	 * Spinlock (locked via CAS) used when resizing and/or creating Cells.
	 *
	 * 把cellsBusy从0置为1的线程才能对cells进行初始化和扩容,以及往cells里放置元素
	 */
	volatile int cellsBusy;
	public void add(long x) {
		Cell[] as;
		long b, v;
		int m;
		Cell a;

		if ((as = cells) != null || !casBase(b = base, b + x)) {
		
			// 如果cells不是null,只能说竞争曾经很激烈,但无法判断当前竞争是否激烈,不过还是会进来
			// 如果cells是null,那会对base尝试一次CAS,成功的话说明竞争不激烈,就不进来了。但失败了就会认为此时竞争蛮激烈,会进来
			
			// 结论:除非能明确判断竞争不激烈,否则都会进来
		
			boolean uncontended = true; // 记录CAS的执行结果
			
			if (as == null || (m = as.length - 1) < 0
					|| (a = as[getProbe() & m]) == null
					|| !(uncontended = a.cas(v = a.value, v + x)))
				// 如果as是null,就说明上一个if条件之所以成立,是因为CAS失败,说明当时竞争激烈
				// 如果as不是null,就随机选一个箱子,try一次CAS。如果CAS成功,那就说明竞争不激烈;如果CAS失败,那就说明竞争很激烈
				// 在as不是null的情况下,“(m = as.length - 1) < 0”和“(a = as[getProbe() & m]) == null”的作用更多是给变量m和a赋值并充当卫语句,为CAS服务
				longAccumulate(x, null, uncontended);
		}
	}
	/**
	 * Returns the probe value for the current thread. Duplicated from
	 * ThreadLocalRandom because of packaging restrictions.
	 *
	 * 可以简单理解为获取一个线程安全的随机数
	 */
	static final int getProbe() {
		return UNSAFE.getInt(Thread.currentThread(), PROBE);
	}
	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; // 从cells中随机选的一个元素
			int n; // cells的长度
			long v; // 从cells中选出来的元素的初值
			
			if ((as = cells) != null && (n = as.length) > 0) {
				
				// cells已经被初始化
				
				if ((a = as[(n - 1) & h]) == null) {
					
					// 随机选择的数组下标没有元素
					
					if (cellsBusy == 0) { // Try to attach new Cell
						Cell r = new Cell(x); // Optimistically create => 把这一句放到“Recheck under lock”里是不是更好?
						
						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; // 当前线程选中的数组下标没有元素,且抢不到cellsBusy
				} 
				
				// 往下的逻辑:随机选择的数组下标有元素
				
				else if (!wasUncontended) // CAS already known to fail
					// wasUncontended为false,当前线程会更新随机数,重选数组下标,这样设计的意图也许是想尽可能选择没有元素的数组下标吧
					wasUncontended = true; // Continue after rehash
				
				else if (a.cas(v = a.value,
						((fn == null) ? v + x : fn.applyAsLong(v, x))))
					break;
				
				// 往下的逻辑:当前线程未能成功地在选中的数组元素(元素不是null)上进行CAS操作
				
				else if (n >= NCPU || cells != as)
					
					// “cells != as”说明其他线程完成了对cells的扩容,让当前线程重来
					// “n >= NCPU”说明没必要再扩容。毕竟同时运行的线程数受制于CPU数量,让当前线程重来
					
					collide = false; // At max size or stale
				
				// 往下的逻辑:其他线程未对cells进行扩容,但其实还可以继续对cells进行扩容
				
				else if (!collide)
					collide = true; // 当前线程第一次在选中的数组元素(元素不是null)上进行CAS操作失败了
				
				// 往下的逻辑:当前线程连续两次在选中的数组元素(元素不是null)上进行CAS操作都失败了
				
				else if (cellsBusy == 0 && casCellsBusy()) {
					
					// 当前线程对cells进行扩容
					
					try {
						if (cells == as) { // Expand table unless stale
							
							// 其他线程未对cells进行扩容
							
							Cell[] rs = new Cell[n << 1];
							
							for (int i = 0; i < n; ++i)
								rs[i] = as[i];
								
							cells = rs;
						}
					} finally {
						cellsBusy = 0;
					}
					
					collide = false;
					continue; // Retry with expanded table
				}
				
				h = advanceProbe(h); // 更新随机数,用于重选数组下标
				
			} 
			
			// cells是null
			
			else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
				
				// 只有一个线程能进来初始化cells
				
				boolean init = false;
				try { // Initialize table
					if (cells == as) {
						
						// 其他线程未对cells进行初始化
						
						Cell[] rs = new Cell[2]; // cell数组初始长度是2
						rs[h & 1] = new Cell(x); // 只初始化1个数组元素,并赋值
						cells = rs;
						init = true;
					}
				} finally {
					cellsBusy = 0;
				}
				
				if (init)
					break; // 跳出for循环
			} 
			
			else if (casBase(v = base,
					((fn == null) ? v + x : fn.applyAsLong(v, x)))) // 尝试在base上操作
				break; // Fall back on using base
		}
	}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值