多线程笔记整理(草稿)

进程、线程

概念

  • 进程就是进行中的程序,它是个动态的概念。是系统进行资源分配与调度的基本单位.
  • 线程就是进程中并发执行的一个子任务。
  • 程序计数器和运行栈是线程私有,共享内存空间和父进程资源。
  • ThreadPoolTaskExecutor线程池拿到线程,然后CompletionService<Long> completionService = new ExecutorCompletionService<Long>(threadPoolTaskExecutor) 得到CompletionService做异步执行
  • Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。completionService.submit(new Callable) 然后completionService.take()拿到Feature, future.get()得到结果

守护线程

  • 当线程只剩下守护线程的时候,JVM就会退出;补充一点如果还有其他的任意一个用户线程还在,JVM就不会退出
  • thread.setDaemon(true)必须在thread.start()之前设置
  • 作用:当主线程结束时,结束其余的子线程(守护线程)自动关闭,就免去了还要一个个关闭的麻烦。

CAS

CAS 机制:

  • 适用场景:乐观认为并发不高,不需要阻塞,可以不上锁。
  • 特点:一般使用中常用自旋,不断比较更新,直到成功。
  • 缺点:高并发cpu压力大;ABA问题
  • 关联:在AQS和Atomic相关类中也是使用到了CAS机制。

公平锁与非公平锁

  • 先对锁进行请求的就一定先获取到锁,那么这就是公平锁,反之是非公平锁。
  • 非公平锁机制的效率往往会胜过公平锁的原因是,恢复一个被挂起的线程与该线程真正运行之间存在着严重的延迟.
  • 持有锁的时间相对较长或者请求锁的平均间隔较长,应该使用公平锁。因为这些情况下,插队带来的吞吐量提升可能不会出现

AbstractQueuedSynchronizer

获取锁思路

AQS 在获取锁的思路是,先尝试直接获取锁,如果有另一个线程持有锁或者有其他线程在等待队列中等待这个锁,则会失败并将当前线程放在队列中,按照 FIFO 的原则等待锁。

final void lock() { acquire(1);}

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
  • addWaiter是线程入队之前包装成Node,然后追加到队尾。
  1. 创建好Node后,如果队列不为空,使用cas的方式将Node加入到队列尾。注意,这里只执行了一次修改操作,并且可能因为并发的原因失败。因此修改失败的情况和队列为空的情况,需要进入enq。
  2. enq是个死循环,保证Node一定能插入队列。注意到,当队列为空时,会先为头节点创建一个空的Node,因为头节点代表获取了锁的线程,现在还没有,所以先空着。
  • 调用acquireQueued阻塞线程
  1. 这里先自旋几次,不成功在阻塞
  2. 如果前一个节点正好是head,表示自己排在第一位,可以马上调用tryAcquire尝试。如果获取成功就简单了,直接修改自己为head(这步是实现公平锁的核心,保证释放锁时,由下个排队线程获取锁)。
  3. 自选几次后如果还是不能获取锁就挂起,直到上一个线程release后释放锁并打断该线程。继续自旋,此时就可以tryAcquire成功,然后进行处理了。

参考:https://www.jianshu.com/p/fe027772e156

释放锁思路

唤醒后继节点,后继节点为nul则需要跳过该节点从tail节点开始。这是因为:

为何后继节点为null: 存在超时、被中断的情况
为何不是从node.next开始: 在于node.next仍然可能会存在null或者取消了,所以采用tail回溯办法找第一个可用的线程。最后调用LockSupport的unpark(Thread thread)方法唤醒该线程

独占锁

ReetrantLock 有且只有一个线程获取到锁,其余线程全部挂起,直到该拥有锁的线程释放锁,被挂起的线程被唤醒重新开始竞争锁

共享锁

CountDownLatch

读写锁

ReentrantReadWriteLock、StampedLock

共享和独占的相同和不同点

与 AQS 的独占功能一样,共享锁是否可以被获取的判断为空方法,交由子类去实现。
与 AQS 的独占功能不同,当锁被头节点获取后,独占功能是只有头节点获取锁,其余节点的线程继续沉睡,等待锁被释放后,才会唤醒下一个节点的线程,而共享功能是只要头节点获取锁成功,就在唤醒自身节点对应的线程的同时,继续唤醒 AQS 队列中的下一个节点的线程,每个节点在唤醒自身的同时还会唤醒下一个节点对应的线程,以实现共享状态的“向后传播”,从而实现共享功能

自旋??

condition实现原理

  1. 首先,线程1调用lock.lock()时,由于此时锁并没有被其它线程占用,因此线程1直接获得锁并不会进入AQS同步队列中进行等待。
  2. 在线程1执行期间,线程2调用lock.lock()时由于锁已经被线程1占用,因此,线程2进入AQS同步队列中进行等待。
  3. 在线程1中执行condition.await()方法后,线程1释放锁并进入条件队列Condition中等待signal信号的到来
  4. 线程2,因为线程1释放锁的关系,会唤醒AQS队列中的头结点,所以线程2会获取到锁。
  5. 线程2调用signal方法,这个时候Condition的等待队列中只有线程1一个节点,于是它被取出来,并被加入到AQS的等待队列中。注意,这个时候,线程1 并没有被唤醒,只是加入到了AQS等待队列中去了
  6. 待线程2执行完成之后并调用lock.unlock()释放锁之后,会唤醒此时在AQS队列中的头结点.所以线程1开始争夺锁(由于此时只有线程1在AQS队列中,因此没人与其争夺),如果获得锁继续执行。
    直到线程1释放锁整个过程执行完毕。
    可以看到,整个协作过程是靠结点在AQS的等待队列和Condition的等待队列中来回移动实现的,Condition作为一个条件类,很好的自己维护了一个等待信号的队列,并在适时的时候将结点加入到AQS的等待队列中来实现的唤醒操作
    原文:https://blog.csdn.net/u010412719/article/details/52089561

acquire和acquireInterruptibly的区别

在acquire中,如果park操作被中断,那么只是记录了interrupted状态,然后继续进入循环判断是否可以acquire或者阻塞。而在acquireInterruptibly中,一旦被中断,那么就立即抛出InterruptedException异常

更多参见:https://www.infoq.cn/article/java8-abstractqueuedsynchronizer

ReetrantLock

  • 默认非公平锁.可通过 其构造器设置为公平锁。
public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
  • 公平的锁原理是:老老实实的开始就走 AQS 的流程排队获取锁

  • 非公平锁原理是:在 lock 的时候先直接 cas 修改一次 state 变量(尝试获取锁),成功就返回,不成功再走 AQS 的流程。

  • 公平锁和非公平锁区别:


    image.png
  • ReetrantLock支持对同一线程重加锁,但是加锁多少次,就必须解锁多少次,这样才可以成功释放锁

  • ReentrantLock提供了多样化的同步特性,如超时获取锁、可以被中断获取锁(synchronized的同步是不能中断的)、等待唤醒机制的多个条件变量(Condition)等

  • Condition能够精细的控制多线程的休眠与唤醒。

  • 对于一个锁,我们可以为多个线程间建立不同的Condition

  • AbstractQueuedSynchronizer又称为队列同步器,内部通过一个int类型的成员变量state来控制同步状态.内部类Node线程获取锁的排队工作,同时利用内部类ConditionObject构建等待队列.

动态代理

  • newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

并发辅助类

CountDownLatch

它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能

CyclicBarrier

字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。我们暂且把这个状态就叫做barrier,当调用await()方法之后,线程就处于barrier了。

Semaphore

字面意思为 信号量,Semaphore可以控同时访问的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。

线程安全容器

ConcurrentHashMap、ConcurrentSkipListMap、CopyOnWriteArrayList

并发队列

ArrayBlockingQueue、ConcurrentLinkedQueue、ConcurrentLinkedQueue、SynchronousQueue、PriorityBlockingQueue

image.png

参考:https://blog.csdn.net/vernonzheng/article/details/8247564

ArrayBlockingQueue

有界队列,基于数组实现的阻塞队列

LinkedBlockingQueue

其实也是有界队列,但是不设置大小时就时Integer.MAX_VALUE,内部是基于链表实现的

  • ArrayBlockingQueue 与 LinkedBlockingQueue 对比
    ArrayBlockingQueue 实现简单,表现稳定,添加和删除使用同一个锁,通常性能不如后者
    LinkedBlockingQueue 添加和删除两把锁是分开的,所以竞争会小一些

PriorityBlockingQueue

具有优先级的阻塞队列,无界队列

ConcurrentLinkedQueue

无锁队列,底层使用CAS操作,通常具有较高吞吐量,但是具有读性能的不确定性,弱一致性——不存在如ArrayList等集合类的并发修改异常,通俗的说就是遍历时修改不会抛异常

SynchronousQueue

比较奇葩,内部容量为零,适用于元素数量少的场景,尤其特别适合做交换数据用,内部使用 队列来实现公平性的调度,使用栈来实现非公平的调度,在Java6时使用CAS代替了替换了原来的锁逻辑,它是Executors.newCachedThreadPool()的默认队列。
参见:https://blog.csdn.net/yanyan19880509/article/details/52562039

Executor框架

线程池

参考:https://www.jianshu.com/p/ade771d2c9c0

ExecutorService

CompletionService

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容