并发编程

同步容器

1、同步容器的思路:隐藏状态、对象访问加锁
2、同步容器单个操作都是线程安全的,但是复合操作不是线程安全的,需要进行客户端枷锁来解决,即对容器自身加锁。同步容器
3、同步修改异常和迭代器。当使用iterator来迭代容器的时候,如果其他线程同时修改了这个容器,那么在调用hasNext,next等方法的时候会抛出这个异常,如果不是使用迭代器来迭代就不会出现这个问题。为了防止抛出这个异常,有2个方法,一个是在迭代期间对容器加锁,一个是复制一个副本到线程中。

并发容器

1、ConcurrenthashMap 是用来解决同步hashmap在并发环境下访问性能的低下而设计的。主要采用锁分段技术,细化加锁粒度,降低锁竞争。
2、CopyOnWriteArrayList、CopyOnWriteArraySet 采用CopyOnWrite的机制,读写分离的思路,当向容器里添加元素时copy一份,在新的容器里添加元素,最后在将原来的引用指向新的容器。也就是说容器一旦发布出去后,就是一个事实不可变的额,这就可以实现并发读的使用不需要加锁。添加的时候需要加锁,所以他的使用场景是读远远大于写的场景,比如事件通知系统、黑名单管理服务。并且需要注意,写时复制会占用更多的内容,可能会触发大量的youngGC和fullGC,并且只能保证最终一致性。
3、阻塞队列和生产者消费者模式,提供了put、take方法和超时的offer和poll方法,主要功能在于解耦。优先队列和同步队列

同步工具

同步工具的作用是可以根据自身的状态来协调不同线程的控制流。blockingqueue(阻塞队列)、semaphore(信号量)、barrier(栅栏)、latch(闭锁)。这些工具都分装了一些状态,并可以根据这些状态来决定执行同步工具的线程是等待还是执行
1、闭锁。初始化一个数量,countDown方法将数量减1,await方法将当前线程在闭锁上等待,当数量为0的时候锁打开。a、如果主线程await,多个子线程countdown,那么可以实现当子线程都全部就绪通知主线程继续。b、使用二元闭锁,如果主线程countdown,子线程await,可以实现当主线程相关工作准备就绪后通知所有子线程同时开始运行
2、FutureTask
也可以做闭锁,是当前线程等待执行callable的线程返回结果,否则一直等待
3、信号量 semaphore 用来控制同时访问某个资源的线程数量,初始化一个数量,acquire申请一个许可,release释放一个许可,如果没有许可可以用accquire将阻塞
4、栅栏

任务取消

0、可以使用自定义变量作为中断标志,然后轮询检查这个状态,问题是有可能在处理过程中发生阻塞后不能及时或者永远不能检查这个标志,导致线程不能退出,任务不能结束。
1、使用中断,中断是线程之间的一种协作机制
interrupt 中断一个线程,设置他的中断标志位
isInterrupted 返回目标线程的中断标志位
interrupted 清除当前线程的中断标志位
JVM在什么时候感知到中断标志为的变化?会不会和自定义变量一样发生严重延迟问题?
JVM不能保证阻塞的方法检测中断的速度,但在实际情况中响应速度是非常快的!!

JVM的关闭

jvm的关闭分为正常关闭和强行关闭。正常关闭包括所有线程(非守护)任务都执行完成并退出、使用System.exit、或者使用ctrl-c。强行关闭包括使用Runtime.halt(),在操作系统中使用kill -9
正常关闭中,jvm首先调用注册的关闭钩子 shutdown hook

饥饿死锁

一个任务等待另外一个任务完成,但是另外一个任务又在等第一个任务完成才能完成。
比如:在单线程线程池中,一个在线程池中的任务把另一个任务提交到当前线程池中,但是提交不进去阻塞,这样就发生了饥饿死锁

线程池

ThreadPoolExecutor 线程池,所有其他的线程池都是根据这个来配置的。
线程池本质上就是利用一些基础构建工具来实现的一种工具而已。线程池中包含两个数据结构:任务队列、工作者队列,提交的任务都是先放在任务队列中(同步队列除外),任务队列容纳worker对象。处理的时候线程不断的从任务队列中去取任务来执行。

线程池的5大参数:coreSize、maxSize、keepAlive、workqueue、threadFactory、饱和策略
其中coreSize、maxSize、keepAlive负责控制线程的创建和销毁

新请求到来:

当前线程数< coreSize ,创建新的线程来处理对象,即使还有其他的线程处于空闲状态
coreSize<当前线程数<maxSize , 仅仅当任务队列满的情况下才会创建新线程去处理
coreSize的含义是线程池的目标大小,线程池觉得coreSize这么多的线程已经可以处理queueSize中的所有任务了,所以如果线程数为coreSize,但是任务队列还没有满的时候是不会创建新线程的。

Paste_Image.png

一个新任务来了,线程池有三种处理方式:1、新建线程立即处理,2、存到任务队列中,3、拒绝处理。新建线程有2种情况,一种是核心线程未满,一种是核心线程满了但未达到最大线程大小并且任务队列满了。

新建线程是怎么过程?

提交一个任务后,内部会将任务封装到Worker里面,然后把worker存到内部一个hashset里面(works),work是一个线程对象,他run里主要的逻辑就是不断的从任务队列中取任务来执行,这是一个无限循环。

线程退出的过程是怎么过程?

1、在超过keepAlive 且curSize>coreSize 情况下退出(正常退出)
2、线程抛出异常退出(非正常退出)
在这两种情况下分别做什么处理?如果线程池所有的线程都抛异常退出,那么是不是线程池就没有线程了?
如果是抛异常非正常退出,那么会在补一个线程进去
如果是正常退出,1、如果不允许核心线程超时,那么最少保留的线程就是coreSize,如果允许核心线程超时并且这是任务队列还有任务,那么最少保留一个线程,如果当前线程退出后小于最小线程数,那么会补一个线程,否则不会补。

我们知道线程池在构造完成后,可以通过setter来改变他的参数,如 coreSize、maxSize等等,如果对于SingleThread 单线程池来说在构造完成后,我们去改变他的线程数,不就可以让他变成非单线程池了么?单线程的语义在于让所有任务串行执行,那么这样一来不就打破原有的语义了么?

通过线程封闭来实现不可修改,即把ThreadPoolExecutor 实例作为另外一个类 FinalizableDelegatedExecutorService 的实例属性,对外是没有setter方法可以设置的。

newFixedThreadPool(1) 和 newSingleThreadExecutor() 都是一个线程,他们一样吗??

不一样,newSingleThreadExecutor 可以保证构造完成之后参数不会被修改,保证单线程串行执行语义

如果newSingleThreadExecutor中唯一的一个线程挂了之后怎么办?

和上边一样,单线程池coreSize是1,如果这个线程挂了,会在补充一个线程进去,从而保证始终只有一个线程在运行。

同样在线程空闲时间超过了keepAlive的情况下,1、coreSize<curSize<maxSize 线程就会退出,2、curSize<coreSize 线程不同退出,但这是如果实现的?

worker唯一的逻辑就是不停的从任务队列中取任务,执行任务,任务队列本身是个blockingqueue,当从blockingqueue中取任务的时候,poll 可以有超时返回的特性,take 具有阻塞的特性,正式利用这两个特性来实现两种情况下的处理。对于第一种情况,取任务的时候使用poll,超时的时间就是keepAlive,如果在keepAlive内没有渠道任务返回null,那么线程退出循环,这样就实现了在超过keepAlive的情况下线程退出。对于第二种情况,取任务的时候采用take的方式,一直阻塞直到有任务,这样即使超时也不会退出。

在默认情况下,新创建的线程池是没有线程的,只有请求到来的时候才会创建线程。也可以配置预分配coreSize的线程preStartCoreThreads()

加入coreSize=0,任务队列长度是10,那么只有当任务队列满的时候才开始新建线程,才开始执行任务。

线程池中使用的任务队列有3中:有界、无界、同步移交

当有界队列满的时候,会采取饱和策略,jdk提供了4中饱和策略:终止策略、调用者运行、抛弃、抛弃旧的。

几种常见的线程池内部实现。

FixedThreadPool:coreSize=maxSize,存活时间=0,任务队列使用的是无界队列 LinkedBlockingQueue。
CachedThreadPool:coreSize=0,maxSize=Integer.MAX_VALUE,keepAlive=60s,使用同步队列
SingleThread:coreSize=maxSize=1,keepAlive=1ms,使用无界队列LinkedBlockingQueue

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

推荐阅读更多精彩内容