自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(299)
  • 资源 (3)
  • 收藏
  • 关注

原创 ConcurrentHashMap的源码分析-fullAddCount源码分析

fullAddCount主要是用来初始化CounterCell,来记录元素个数,里面包含扩容,初始化等操作private final void fullAddCount(long x, boolean wasUncontended) { int h; //获取当前线程的probe的值,如果值为0,则初始化当前线程的probe的值,probe就是随机数 if ((h = ThreadLocalRandom.getProbe()) == 0) { ThreadLocalRandom.loc

2020-12-30 20:26:32 321

原创 ConcurrentHashMap的源码分析-CounterCells解释

ConcurrentHashMap是采用CounterCell数组来记录元素个数的,像一般的集合记录集合大小,直接定义一个size的成员变量即可,当出现改变的时候只要更新这个变量就行。为什么ConcurrentHashMap要用这种形式来处理呢?问题还是处在并发上,ConcurrentHashMap是并发集合,如果用一个成员变量来统计元素个数的话,为了保证并发情况下共享变量的的难全兴,势必会需要通过加锁或者自旋来实现,如果竞争比较激烈的情况下,size的设置上会出现比较大的冲突反而影响了性能,所以在Co

2020-12-30 20:21:29 681

原创 ConcurrentHashMap的源码分析-addCount

在putVal最后调用addCount的时候,传递了两个参数,分别是1和binCount(链表长度),看看addCount方法里面做了什么操作x表示这次需要在表中增加的元素个数,check参数表示是否需要进行扩容检查,大于等于0都需要进行检查private final void addCount(long x, int check) { CounterCell[] as; long b, s; 判断counterCells是否为空, 1. 如果为空,就通过cas操作尝试修改baseCoun

2020-12-30 20:19:18 275

原创 ConcurrentHashMap的源码分析-put方法第二阶段

在putVal方法执行完成以后,会通过addCount来增加ConcurrentHashMap中的元素个数,并且还会可能触发扩容操作。这里会有两个非常经典的设计1. 高并发下的扩容2. 如何保证addCount的数据安全性以及性能//将当前ConcurrentHashMap的元素数量加1,有可能触发transfer操作(扩容) addCount(1L, binCount); return null; ...

2020-12-30 20:17:13 86

原创 ConcurrentHashMap的源码分析-tabAt

该方法获取对象中offset偏移地址对应的对象field的值。实际上这段代码的含义等价于tab[i],但是为什么不直接使用tab[i]来计算呢?getObjectVolatile,一旦看到volatile关键字,就表示可见性。因为对volatile写操作happen-before于volatile读操作,因此其他线程对table的修改均对get读取可见;虽然table数组本身是增加了volatile属性,但是“volatile的数组只针对数组的引用具有volatile的语义,而不是它的元素”。所..

2020-12-30 20:16:00 315

原创 ConcurrentHashMap的源码分析-initTable

数组初始化方法,这个方法比较简单,就是初始化一个合适大小的数组sizeCtl这个要单独说一下,如果没搞懂这个属性的意义,可能会被搞晕这个标志是在Node数组初始化或者扩容的时候的一个控制位标识,负数代表正在进行初始化或者扩容操作-1代表正在初始化-N代表有N-1有二个线程正在进行扩容操作,这里不是简单的理解成n个线程,sizeCtl就是-N,这块后续在讲扩容的时候会说明0标识Node数组还没有被初始化,正数代表初始化或者下一次扩容的大小private final Node<K..

2020-12-30 20:13:44 175

原创 ConcurrentHashMap的源码分析-JDK1.7和Jdk1.8版本的变化

ConcurrentHashMap和HashMap的实现原理是差不多的,但是因为ConcurrentHashMap需要支持并发操作,所以在实现上要比hashmap稍微复杂一些。在JDK1.7的实现上,ConrruentHashMap由一个个Segment组成,简单来说,ConcurrentHashMap是一个Segment数组,它通过继承ReentrantLock来进行加锁,通过每次锁住一个segment来保证每个segment内的操作的线程安全性从而实现全局线程安全。整个结构图如下当每个操作分

2020-12-30 20:11:04 124

原创 ConcurrentHashMap的初步使用及场景

CHM的使用ConcurrentHashMap是J.U.C包里面提供的一个线程安全并且高效的HashMap,所以ConcurrentHashMap在并发编程的场景中使用的频率比较高,那么这一节课我们就从ConcurrentHashMap的使用上以及源码层面来分析ConcurrentHashMap到底是如何实现安全性的api使用ConcurrentHashMap是Map的派生类,所以api基本和Hashmap是类似,主要就是put、get这些方法,接下来基于ConcurrentHashMap的pu

2020-12-30 20:04:07 1149 1

原创 CyclicBarrier

CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续工作。CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier当前线程已经到达了屏障,然后当前线程被阻塞使用场景当存在需要所有的子任务都完成时,才执行.

2020-12-30 19:58:52 106

原创 Semaphore源码分析

从Semaphore的功能来看,我们基本能猜测到它的底层实现一定是基于AQS的共享所,因为需要实现多个线程共享一个领排池创建Semaphore实例的时候,需要一个参数permits,这个基本上可以确定是设置给AQS的state的,然后每个线程调用acquire的时候,执行state = state-1,release的时候执行state = state + 1,当然,acquire的时候,如果state = 0,说明没有资源了,需要等待其他线程release。Se...

2020-12-29 22:16:07 110

原创 Semaphore

semaphore也就是我们常说的信号灯,semaphore可以控制同时访问的线程个数,通过acquire获取一个许可,如果没有就等待,通过release释放一个许可。有点类似限流的作用。叫信号灯的原因也和他的用处有关,比如某商场就5个停车位,每个停车位只能停一辆车,如果这个时候来了10辆车,必须要等前面有空的车位才能进入。使用案例public class Test { public static void main(String[] args) { Semaphore semapho

2020-12-29 22:11:50 84

原创 setHeadAndPropagate

这个方法的主要作用是把被唤醒的节点,设置成head节点。 然后继续唤醒队列中的其他线程。由于现在队列中有3个线程处于阻塞状态,一旦ThreadA被唤醒,并且设置为head之后,会继续唤醒后续的ThreadBprivate void setHeadAndPropagate(Node node, int propagate) { Node h = head; // Record old head for check below setHead(node); if (propagate &g

2020-12-29 22:09:38 630

原创 doAcquireSharedInterruptibly

一旦ThreadA被唤醒,代码又会继续回到doAcquireSharedInterruptibly中来执行。如果当前state满足=0的条件,则会执行setHeadAndPropagate方法private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.SHARED); boolean failed = true; try {

2020-12-29 22:07:53 976

原创 AQS.doReleaseShared

共享锁的释放和独占锁的释放有一定的差别前面唤醒锁的逻辑和独占锁是一样,先判断头结点是不是SIGNAL状态,如果是,则修改为0,并且唤醒头结点的下一个节点PROPAGATE: 标识为PROPAGATE状态的节点,是共享锁模式下的节点状态,处于这个状态下的节点,会对线程的唤醒进行传播private void doReleaseShared() { for (;;) { Node h = head; if (h != null && h != tail) {

2020-12-29 22:05:03 228

原创 CountDownLatch.countDown

由于线程被await方法阻塞了,所以只有等到countdown方法使得state=0的时候才会被唤醒,我们来看看countdown做了什么1. 只有当 state 减为 0 的时候,tryReleaseShared 才返回 true, 否则只是简单的 state = state - 12. 如果state=0, 则调用doReleaseShared唤醒处于await状态下的线程public final boolean releaseShared(int arg) { if (tryRel.

2020-12-29 22:02:25 282 1

原创 doAcquireSharedInterruptibly

1. addWaiter设置为shared模式。2. tryAcquire和tryAcquireShared的返回值不同,因此会多出一个判断过程3. 在判断前驱节点是头节点后,调用了setHeadAndPropagate方法,而不是简单的更新一下头节点。private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.SHARED

2020-12-29 22:00:23 485

原创 acquireSharedInterruptibly

countdownlatch也用到了AQS,在CountDownLatch内部写了一个Sync并且继承了AQS这个抽象类重写了AQS中的共享锁方法。首先看到下面这个代码,这块代码主要是判断当前线程是否获取到了共享锁;(在CountDownLatch中,使用的是共享锁机制,因为CountDownLatch并不需要实现互斥的特性)public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if

2020-12-29 21:57:33 877

原创 Condition总结-CountDownLatch源码分析

对于CountDownLatch,我们仅仅需要关心两个方法,一个是countDown()方法,另一个是await()方法。countDown()方法每次调用都会将state减1,直到state的值为0;而await是一个阻塞方法,当state减为0的时候,await方法才会返回。await可以被多个线程调用,大家在这个时候脑子里要有个图:所有调用了await方法的线程阻塞在AQS的阻塞队列中,等待条件满足(state == 0),将线程从队列中一个...

2020-12-29 21:56:12 120

原创 Condition总结-CountDownLatch

countdownlatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完毕再执行。从命名可以解读到countdown是倒数的意思,类似于我们倒计时的概念countdownlatch提供了两个方法,一个是countDown,一个是await,countdownlatch初始化的时候需要传入一个整数,在这个整数倒数到0之前,调用了await方法的程序都必须要等待,然后通过countDown来倒数。使用案例public static void main(String[].

2020-12-29 21:54:52 129

原创 Condition总结-await和signal的总结

我把前面的整个分解的图再通过一张整体的结构图来表述,线程awaitThread先通过lock.lock()方法获取锁成功后调用了condition.await方法进入等待队列,而另一个线程signalThread通过lock.lock()方法获取锁成功后调用了condition.signal或者signalAll方法,使得线程awaitThread能够有机会移入到同步队列中,当其他线程释放lock后使得线程awaitThread能够有机会获取lock,从而使得线程awaitThread能够从await方法中

2020-12-28 23:18:08 278

原创 reportInterruptAfterWait

根据checkInterruptWhileWaiting方法返回的中断标识来进行中断上报。如果是THROW_IE,则抛出中断异常如果是REINTERRUPT,则重新响应中断private void reportInterruptAfterWait(int interruptMode) throws InterruptedException { if (interruptMode == THROW_IE) throw new InterruptedException(); els.

2020-12-28 23:15:31 523

原创 acquireQueued

这个方法在讲aqs的时候说过,是的当前被唤醒的节点ThreadA去抢占同步锁。并且要恢复到原本的重入次数状态。调用完这个方法之后

2020-12-28 23:13:27 664 1

原创 checkInterruptWhileWaiting

如果当前线程被中断,则调用transferAfterCancelledWait方法判断后续的处理应该是抛出InterruptedException还是重新中断。这里需要注意的地方是,如果第一次CAS失败了,则不能判断当前线程是先进行了中断还是先进行了signal方法的调用,可能是先执行了signal然后中断,也可能是先执行了中断,后执行了signal,当然,这两个操作肯定是发生在CAS之前。这时需要做的就是等待当前线程的node被添加到AQS队列后,也就是enq方法返回后,返回false告诉checkI

2020-12-28 23:12:22 752

原创 被阻塞的线程唤醒后的逻辑

前面在分析await方法时,线程会被阻塞。而通过signal被唤醒之后又继续回到上次执行的逻辑中标注为红色部分的代码checkInterrupt WhileWaiting这个方法是干嘛呢?其实从名字就可以看出来,就是ThreadA在condition队列被阻塞的过程中,有没有被其他线程触发过中断请求public final void await() throws InterruptedException { if (Thread.interrupted()) throw new Inter

2020-12-28 23:08:55 154

原创 AQS.transferForSignal

该方法先是CAS修改了节点状态,如果成功,就将这个节点放到AQS队列中,然后唤醒这个节点上的线程。此时,那个节点就会在await方法中苏醒final boolean transferForSignal(Node node) { if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))//更新节点的状态为0,如果更新失 败,只有一种可能就是节点被CANCELLED了 return false; Node p = enq(...

2020-12-28 23:06:46 414

原创 Condition.doSignal

对condition队列中从首部开始的第一个condition状态的节点,执行transferForSignal操作,将node从condition队列中转换到AQS队列中,同时修改AQS队列中原先尾节点的状态private void doSignal(Node first) { do { //从Condition队列中删除first节点 if ( (firstWaiter = first.nextWaiter) == null) lastWaiter = null; // 将

2020-12-28 23:04:02 122

原创 Condition.signal

await方法会阻塞ThreadA,然后ThreadB抢占到了锁获得了执行权限,这个时候在ThreadB中调用了Condition的signal()方法,将会唤醒在等待队列中节点public final void signal() { if (!isHeldExclusively()) //先判断当前线程是否获得了锁,这个判断比较简单,直接用获得锁的线程和当前线程相比即可 throw new IllegalMonitorStateException(); Node first = fi

2020-12-28 23:02:11 763

原创 isOnSyncQueue

判断当前节点是否在同步队列中,返回false表示不在,返回true表示在如果不在AQS同步队列,说明当前节点没有唤醒去争抢同步锁,所以需要把当前线程阻塞起来,直到其他的线程调用signal唤醒如果在AQS同步队列,意味着它需要去竞争同步锁去获得执行程序执行权限为什么要做这个判断呢?原因是在condition队列中的节点会重新加入到AQS队列去竞争锁。也就是当调用signal的时候,会把当前节点从condition队列转移到AQS队列大家思考一下,基于现在的逻辑结构。如何去判断ThreadA这

2020-12-28 23:00:52 443

原创 fullyRelease

fullRelease,就是彻底的释放锁,什么叫彻底呢,就是如果当前锁存在多次重入,那么在这个方法中只需要释放一次就会把所有的重入次数归零。final int fullyRelease(Node node) { boolean failed = true; try { int savedState = getState(); //获得重入的次数 if (release(savedState)) {// 释放锁并且唤醒下一个同步队列中的线程 failed = fa

2020-12-28 22:57:50 800

原创 addConditionWaiter

这个方法的主要作用是把当前线程封装成Node,添加到等待队列。这里的队列不再是双向链表,而是单向链表private Node addConditionWaiter() { Node t = lastWaiter; // 如果lastWaiter不等于空并且waitStatus不等于CONDITION时,把冲好这个节点从链表中移除 if (t != null && t.waitStatus != Node.CONDITION) { unlinkCancelledWait

2020-12-27 22:03:23 388

原创 condition.await

调用Condition的await()方法(或者以await开头的方法),会使当前线程进入等待队列并释放锁,同时线程状态变为等待状态。当从await()方法返回时,当前线程一定获取了Condition相关联的锁public final void await() throws InterruptedException { if (Thread.interrupted()) //表示await允许被 中断 throw new InterruptedException(); Node node =

2020-12-27 22:01:34 1851

原创 Condition源码分析

调用Condition,需要获得Lock锁,所以意味着会存在一个AQS同步队列,在上面那个案例中,假如两个线程同时运行的话,那么AQS的队列可能是下面这种情况那么这个时候ThreadA调用了condition.await方法,它做了什么事情呢?...

2020-12-27 21:59:02 101

原创 Condition的基本使用-ConditionSignal

public class ConditionDemoSignal implements Runnable{ private Lock lock; private Condition condition; public ConditionDemoSignal(Lock lock, Condition condition){ this.lock=lock; this.condition=condition; } @Override public void run() {.

2020-12-27 21:57:52 746

原创 Condition的基本使用-ConditionWait

public class ConditionDemoWait implements Runnable{ private Lock lock; private Condition condition; public ConditionDemoWait(Lock lock, Condition condition){ this.lock=lock; this.condition=condition; } @Override public void run() { S.

2020-12-27 21:55:59 404

原创 Condition

在前面学习synchronized的时候,有讲到wait/notify的基本使用,结合synchronized可以实现对线程的通信。那么这个时候我就在思考了,既然J.U.C里面提供了锁的实现机制,那J.U.C里面有没有提供类似的线程通信的工具呢?于是找阿找,发现了一个Condition工具类。Condition是一个多线程协调通信的工具类,可以让某些线程一起等待某个条件(condition),只有满足条件时,线程才会被唤醒...

2020-12-27 21:53:36 69

原创 公平锁和非公平锁的区别

锁的公平性是相对于获取锁的顺序而言的,如果是一个公平锁,那么锁的获取顺序就应该符合请求的绝对时间顺序,也就是FIFO。在上面分析的例子来说,只要CAS设置同步状态成功,则表示当前线程获取了锁,而公平锁则不一样,差异点有两个FairSync.tryAcquirefinal void lock() { acquire(1); }非公平锁在获取锁的时候,会先通过CAS进行抢占,而公平锁则不会FairSync.tryAcquireprotected final boolean try.

2020-12-27 21:51:57 128

原创 AQS.acquireQueued

这个方法前面已经完整分析过了,我们只关注一下ThreadB被唤醒以后的执行流程。由于ThreadB的prev节点指向的是head,并且ThreadA已经释放了锁。所以这个时候调用tryAcquire方法时,可以顺利获取到锁1. 把ThreadB节点当成head2. 把原head节点的next节点指向为nullfinal boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { .

2020-12-27 21:49:38 274

原创 原本挂起的线程继续执行

通过ReentrantLock.unlock,原本挂起的线程被唤醒以后继续执行,应该从哪里执行大家还有印象吧。原来被挂起的线程是在acquireQueued方法中,所以被唤醒以后继续从这个方法开始执行

2020-12-27 21:46:56 154

原创 为什么在释放锁的时候是从 tail 进行扫描

这个问题有几个同学问过我,我觉得有必要单独拧出来说一下,我们再回到enq那个方法、在标注为红色部分的代码来看一个新的节点是如何加入到链表中的。1. 将新的节点的prev指向tail2. 通过cas将tail设置为新的节点,因为cas是原子操作所以能够保证线程安全性3. t.next=node;设置原tail的next节点指向新的节点private Node enq(final Node node) { for (;;) { Node t = tail; if (t == .

2020-12-27 21:46:22 172 2

原创 锁的释放流程-unparkSuccessor

private void unparkSuccessor(Node node) { int ws = node.waitStatus;//获得head节点的状态 if (ws < 0) compareAndSetWaitStatus(node, ws, 0);// 设置head节点 状态为0 Node s = node.next;//得到head节点的下一个节点 if (s == null || s.waitStatus > 0) { //如果下一个节点为null或者.

2020-12-26 19:41:52 1597

score.csv (score.csv)

load data local inpath '/export/servers/hivedatas/score.csv' into table score2 partition(year='2018',

2020-02-29

teacher.csv

load data local inpath '/export/servers/hivedatas/student.csv' overwrite into table student;

2020-02-29

student.csv

load data local inpath '/export/servers/hivedatas/student.csv' into table student;

2020-02-29

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除