java concurrent 源码_java 并发(concurrent)包源码分析

参考连接:

持续更新中。。。。。

并发是一种能并行运行多个程序或并行运行一个程序中多个部分的能力。如果程序中一个耗时的任务能以异步或并行的方式运行,那么整个程序的吞吐量和可交互性将大大改善。现代的PC都有多个CPU或一个CPU中有多个核,是否能合理运用多核的能力将成为一个大规模应用程序的关键。

Java多线程相关类的实现都在Java的并发包concurrent,concurrent包主要包含3部分内容,第一个是atomic包,里面主要是一些原子类,比如AtomicInteger、AtomicIntegerArray等;第二个是locks包,里面主要是锁相关的类,比如ReentrantLock、Condition等;第三个就是属于concurrent包的内容,主要包括线程池相关类(Executors)、阻塞集合类(BlockingQueue)、并发Map类(ConcurrentHashMap)等。

a9a4de2c5cf427011ed9c48a48b1b45f.png

/*************************************************************************************************************************************************************/

atomic包

/*************************************************************************************************************************************************************/

JDK1.5中引入了底层的支持,在int、long和对象的引用等类型上都公开了CAS的操作,并且JVM把它们编译为底层硬件提供的最有效的方法,在运行CAS的平台上,运行时把它们编译为相应的机器指令。在java.util.concurrent.atomic包下面的所有的原子变量类型中,比如AtomicInteger,都使用了这些底层的JVM支持为数字类型的引用类型提供一种高效的CAS操作。

Unsafe中的操作一般都是基于CAS来实现的,CAS就是Compare and Swap的意思,比较并操作。很多的cpu直接支持CAS指令。CAS是一项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

下面就以AtomicInteger为例。

在没有AtomicInteger之前,对于一个Integer的线程安全操作,是需要使用同步锁来实现的,当然现在也可以通过ReentrantLock来实现,但是最好最方便的实现方式是采用AtomicInteger。

测试代码:

1 packagecom.collection.test;2

3 importjava.util.concurrent.atomic.AtomicInteger;4

5 /**

6 * 原子类的测试7 */

8 public classAtomicTest {9 private static AtomicInteger atomicInteger = newAtomicInteger();10

11 //获取当前值

12 public static voidgetCurrentValue(){13 System.out.println(atomicInteger.get());//-->0

14 }15

16 //设置value值

17 public static voidsetValue(){18 atomicInteger.set(12);//直接用12覆盖旧值

19 System.out.println(atomicInteger.get());//-->12

20 }21

22 //根据方法名称getAndSet就知道先get,则最后返回的就是旧值,如果get在后,就是返回新值

23 public static voidgetAndSet(){24 System.out.println(atomicInteger.getAndSet(15));//-->12

25 }26

27 public static voidgetAndIncrement(){28 System.out.println(atomicInteger.getAndIncrement());//-->15

29 }30

31 public static voidgetAndDecrement(){32 System.out.println(atomicInteger.getAndDecrement());//-->16

33 }34

35 public static voidgetAndAdd(){36 System.out.println(atomicInteger.getAndAdd(10));//-->15

37 }38

39 public static voidincrementAndGet(){40 System.out.println(atomicInteger.incrementAndGet());//-->26

41 }42

43 public static voiddecrementAndGet(){44 System.out.println(atomicInteger.decrementAndGet());//-->25

45 }46

47 public static voidaddAndGet(){48 System.out.println(atomicInteger.addAndGet(20));//-->45

49 }50

51 public static voidmain(String[] args) {52 AtomicTest test = newAtomicTest();53 test.getCurrentValue();54 test.setValue();55 //返回旧值系列

56 test.getAndSet();57 test.getAndIncrement();58 test.getAndDecrement();59 test.getAndAdd();60 //返回新值系列

61 test.incrementAndGet();62 test.decrementAndGet();63 test.addAndGet();64

65 }66 }

AtomicInteger类的源代码

1 private volatile int value;//初始化值

2

3 /**

4 * 创建一个AtomicInteger,初始值value为initialValue5 */

6 public AtomicInteger(intinitialValue) {7 value =initialValue;8 }9

10 /**

11 * 创建一个AtomicInteger,初始值value为012 */

13 publicAtomicInteger() {14 }15

16 /**

17 * 返回value18 */

19 public final intget() {20 returnvalue;21 }22

23 /**

24 * 为value设值(基于value),而其他操作是基于旧值

26 public final void set(intnewValue) {27 value =newValue;28 }29

30 public final boolean compareAndSet(int expect, intupdate) {31 return unsafe.compareAndSwapInt(this, valueOffset, expect, update);32 }33

34 /**

35 * 基于CAS为旧值设定新值,采用无限循环,直到设置成功为止36 *37 *@return返回旧值38 */

39 public final int getAndSet(intnewValue) {40 for(;;) {41 int current = get();//获取当前值(旧值)

42 if (compareAndSet(current, newValue))//CAS新值替代旧值

43 return current;//返回旧值

44 }45 }46

47 /**

48 * 当前值+1,采用无限循环,直到+1成功为止49 *@returnthe previous value 返回旧值50 */

51 public final intgetAndIncrement() {52 for(;;) {53 int current = get();//获取当前值

54 int next = current + 1;//当前值+1

55 if (compareAndSet(current, next))//基于CAS赋值

56 returncurrent;57 }58 }59

60 /**

61 * 当前值-1,采用无限循环,直到-1成功为止62 *@returnthe previous value 返回旧值63 */

64 public final intgetAndDecrement() {65 for(;;) {66 int current =get();67 int next = current - 1;68 if(compareAndSet(current, next))69 returncurrent;70 }71 }72

73 /**

74 * 当前值+delta,采用无限循环,直到+delta成功为止75 *@returnthe previous value 返回旧值76 */

77 public final int getAndAdd(intdelta) {78 for(;;) {79 int current =get();80 int next = current +delta;81 if(compareAndSet(current, next))82 returncurrent;83 }84 }85

86 /**

87 * 当前值+1, 采用无限循环,直到+1成功为止88 *@returnthe updated value 返回新值89 */

90 public final intincrementAndGet() {91 for(;;) {92 int current =get();93 int next = current + 1;94 if(compareAndSet(current, next))95 return next;//返回新值

96 }97 }98

99 /**

100 * 当前值-1, 采用无限循环,直到-1成功为止101 *@returnthe updated value 返回新值102 */

103 public final intdecrementAndGet() {104 for(;;) {105 int current =get();106 int next = current - 1;107 if(compareAndSet(current, next))108 return next;//返回新值

109 }110 }111

112 /**

113 * 当前值+delta,采用无限循环,直到+delta成功为止114 *@returnthe updated value 返回新值115 */

116 public final int addAndGet(intdelta) {117 for(;;) {118 int current =get();119 int next = current +delta;120 if(compareAndSet(current, next))121 return next;//返回新值

122 }123 }124

125 /**

126 * 获取当前值127 */

128 public intintValue() {129 returnget();130 }

注意:

单步操作:例如set()是直接对value进行操作的,不需要CAS,因为单步操作就是原子操作。

多步操作:例如getAndSet(int newValue)是两步操作-->先获取值,在设置值,所以需要原子化,这里采用CAS实现。

对于方法是返回旧值还是新值,直接看方法是以get开头(返回旧值)还是get结尾(返回新值)就好

CAS:比较CPU内存上的值是不是当前值current,如果是就换成新值update,如果不是,说明获取值之后到设置值之前,该值已经被别人先一步设置过了,此时如果自己再设置值的话,需要在别人修改后的值的基础上去操作,否则就会覆盖别人的修改,所以这个时候会直接返回false,再进行无限循环,重新获取当前值,然后再基于CAS进行加减操作。

如果还是不懂CAS,类比数据库的乐观锁。

1 //setup to use Unsafe.compareAndSwapInt for updates

2 private static final Unsafe unsafe =Unsafe.getUnsafe();3 private static final longvalueOffset;4

5 static{6 try{7 valueOffset =unsafe.objectFieldOffset8 (AtomicInteger.class.getDeclaredField("value"));9 } catch (Exception ex) { throw newError(ex); }10 }11

12 private volatile int value;

这是AtomicInteger的所有属性,其中value存的是当前值,而当前值存放的内存地址可以通过valueOffset来确定。实际上是“value字段相对Java对象的起始地址的偏移量”

1 public final boolean compareAndSet(int expect, intupdate) {2 return unsafe.compareAndSwapInt(this, valueOffset, expect, update);3 }

CAS方法:通过对比“valueOffset上的value”与expect是否相同,来决定是否修改value值为update值。

/*************************************************************************************************************************************************************/

lock包

/*************************************************************************************************************************************************************/

/*************************************************************************************************************************************************************/

concurrent源生包

/*************************************************************************************************************************************************************/

BlockingQueue

BlockingQueue是一个接口,它的实现类有ArrayBlockingQueue、DelayQueue、 LinkedBlockingDeque、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue等,它们的区别主要体现在存储结构上或对元素操作上的不同,但是对于take与put操作的原理,却是类似的。下面的源码以ArrayBlockingQueue和LinkedBlockingDeque为例。

1.ArrayBlockingQueue

BlockingQueue内部有一个ReentrantLock,其生成了两个Condition,在ArrayBlockingQueue的属性声明中可以看见:

1 /**Main lock guarding all access*/

2 finalReentrantLock lock;3 /**Condition for waiting takes*/

4 private finalCondition notEmpty;5 /**Condition for waiting puts*/

6 private finalCondition notFull;7

8 ...9

10 public ArrayBlockingQueue(int capacity, booleanfair) {11 if (capacity <= 0)12 throw newIllegalArgumentException();13 this.items = newObject[capacity];14 lock = newReentrantLock(fair);15 notEmpty =lock.newCondition();16 notFull =lock.newCondition();17 }

而如果能把notEmpty、notFull、put线程、take线程拟人的话,那么我想put与take操作可能会是下面这种流程:

put(e)

509acb07ff3cbe0fe451eaa91034bbad.png

take()

8a5a34a426d49109ca08829dd87f3cde.png

其中ArrayBlockingQueue.put(E e)源码如下(其中中文注释为自定义注释,下同):

1 /**

2 * Inserts the specified element at the tail of this queue, waiting3 * for space to become available if the queue is full.4 *5 *@throwsInterruptedException {@inheritDoc}6 *@throwsNullPointerException {@inheritDoc}7 */

8 public void put(E e) throwsInterruptedException {9 checkNotNull(e);10 final ReentrantLock lock = this.lock;11 lock.lockInterruptibly();12 try{13 while (count ==items.length)14 notFull.await(); //如果队列已满,则等待

15 insert(e);16 } finally{17 lock.unlock();18 }19 }20

21 /**

22 * Inserts element at current put position, advances, and signals.23 * Call only when holding lock.24 */

25 private voidinsert(E x) {26 items[putIndex] =x;27 putIndex =inc(putIndex);28 ++count;29 notEmpty.signal(); //有新的元素被插入,通知等待中的取走元素线程

30 }

ArrayBlockingQueue.take()源码如下:

1 public E take() throwsInterruptedException {2 final ReentrantLock lock = this.lock;3 lock.lockInterruptibly();4 try{5 while (count == 0)6 notEmpty.await(); //如果队列为空,则等待

7 returnextract();8 } finally{9 lock.unlock();10 }11 }12

13 /**

14 * Extracts element at current take position, advances, and signals.15 * Call only when holding lock.16 */

17 privateE extract() {18 final Object[] items = this.items;19 E x = this.cast(items[takeIndex]);20 items[takeIndex] = null;21 takeIndex =inc(takeIndex);22 --count;23 notFull.signal(); //有新的元素被取走,通知等待中的插入元素线程

24 returnx;25 }

可以看见,put(E)与take()是同步的,在put操作中,当队列满了,会阻塞put操作,直到队列中有空闲的位置。而在take操作中,当队列为空时,会阻塞take操作,直到队列中有新的元素。

而这里使用两个Condition,则可以避免调用signal()时,会唤醒相同的put或take操作。

以上。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值