(并发三)线程安全框架,结构化并发程序


前言:前两篇读书笔记记录了书本提及的线程安全基本理论概念,同时结合博文和实际的案例进行描述。接下来记录的是线程安全性封装的相关知识。这几章就像索引一样给个全貌(流水账读书笔记 ,没太多可看性),具体知识点细节比如某个并发类源码实现原理或者死锁各种锁的详解需要后续章节抽出来剖析。

一、组合对象

1.设计线程安全的类:
  比起发布状态到公有静态域,将状态封装起来将更易于线程安全性的管理。(如果发布的话请参考上一篇,需要在本类外部进行安全性处理,或者考虑安全发布等比较复杂or局限的操作。)
  设计三要素
   1.1. 找出所有的状态变量:状态自身是否可见安全
   1.2. 找出约束状态变量的不变性:多个状态之间的不变性语义,状态约束,后验条件,先验条件
   1.3. 建立状态变量的并发访问管理策略:管理共享变量
2.线程封闭:


   2.1线程封闭形式之一,监视器模式:对象外包装构建一个安全类,包装类持有该状态对象的唯一引用,保证客户每次调用访问都对该对象上锁。很多线程安全的类都是基于这个模式去实现的。( 源码举例Collections.synchronizedCollection(new ArrayList());)

对象包装上锁,持有唯一引用,封闭线程
    static class SynchronizedCollection<E> implements Collection<E>, Serializable {
        private static final long serialVersionUID = 3053995032091335093L;

        final Collection<E> c;  // Backing Collection
...
...
省略代码
..
 SynchronizedCollection(Collection<E> c) {
            this.c = Objects.requireNonNull(c);
            mutex = this;
        }

        SynchronizedCollection(Collection<E> c, Object mutex) {
            this.c = Objects.requireNonNull(c);
            this.mutex = Objects.requireNonNull(mutex);
        }

        public int size() {
            synchronized (mutex) {return c.size();}
        }
        public boolean isEmpty() {
            synchronized (mutex) {return c.isEmpty();}
        }
        public boolean contains(Object o) {
            synchronized (mutex) {return c.contains(o);}
        }

   2.2:deepcopy,类似Vector源码那样,对可变对象(不安全对象)进行拷贝并赋值到本对象的final成员中,虽然可以实现线程安全,但是影响性能。而且可能出现近期快照问题。


书中举例车辆位置追踪代码
  1. 线程安全性委托:上述内容是从头组装构建一个线程安全类,考虑监视器。但是假设类中大部分组件都是线程安全,那么委托考虑一下。部分情况可以这么做:



    但是假设各个成员状态之间存在某些逻辑上的不变性关系,比如A成员限制要小于B,就要考虑复合操作的线程安全性了。

  2. 在现有安全类中加功能:继承或封装咯,比如看图


  3. 同步策略文档化:略

二、基础构建模块

JAVA自带类库提供了丰富的并发基础构建模块,这一章主要是讲这些

  1. 同步容器:
      之前其实也提过Vector这类早期安全容器。包括Collections.sychronizedXxx等工厂方法创建的类,基本都是封装线程状态,并对公有方法同步。
       1.1. 同步容器类虽然自身线程安全,但是某些情况需要客户端额外加锁。说到烂的复合操作以及语义定义的不变性条件。
       1.2.迭代器与ConcurrentModificationException :一般采用迭代器可以解决单线程下的容器遍历和修改,但是即使是迭代器,遇到并发迭代操作,它们也是及时抛出上述Exception去及时失败。→这可能要求我们迭代的时候给容器上锁(可能有死锁),或者使用克隆(克隆这一步也要上锁)容器。

       1.3. 大概了解有这么回事。System或者Log输出String的时候
  2. 并发容器:ConcurrentHashMap,CopyOnWriteArrayList这些,在遍历操作中代替同步List。也加了一些复合操作。但是问题:不能保证size,isEmpty这些精确。毕竟并发环境下这些计算结果可能失效,只是个近似值。强化的是get,put,containsKey,remove这些。
       2.1. ConcurrentHashMap 无论是看博客还是源码都大概知道是分段锁的思想,这里先概括而过把。后续的章节好像会细讲
       2.2. public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>

    implements ConcurrentMap<K,V>, Serializable

       2.3. CopyOnWriteArrayList 副本保证线程安全,只有当迭代多于操作,才考虑这个。(比如用于事件通知系统,大部分是遍历操作找到监听对象然后调用,而少数才是对监听对象的注册或者注销操作)
  3. 阻塞队列、生产者消费者模式:拆分需要完成的工作以及执行工作。要记录的就这一点把



    补充一个双端队列和工作密取:


  4. 阻塞方法和中断方法:大多数情况只需要传递InterruptedException或者手动调用当前线程的interrupt()。用于阻塞等待线程的额外耗时操作结果比如IO。

  5. 同步工具类:

       5.1. 闭锁:CountDownLatch,使多个线程等待一组事件发生。包含计数器,计数器非0会一直阻塞等待到为0,或者超时或者中断。

    FuctureTask,熟悉线程池和Callable那套API的同学应该不陌生,Fucture对象的get方法调用会阻塞等待子线程执行结果,回调返回自己定义的结果。确保了结果的安全发布。

       5.3. 信号量 Semaphore:

    (mark:不可重入锁,自旋锁,方法级别,如果线程两个方法嵌套调用,或者递归,自旋锁会锁死。可重入锁,线程级别。可重入就意味着:线程可以进入任何一个它已经拥有的锁所同步着的代码块。)
       5.4. 栅栏,类似闭锁。但是CyclicBarrier需要同时到达后执行,闭锁则是等待。比如一家人要一起到齐才算打开栅栏,可以执行下一步比如吃饭。闭锁是来了就吃,count-1。 这个用的不熟。 mark
  1. 构建并发缓存示例:PDF99 忘了就单独去看看 图和码说的很清晰 一步步优化(内置锁处理计算→去除内置锁换用并发ConcurrentMap→有重复计算问题又把Map的V换成Fucture<V>,一旦发现某个线程计算完了就返回future对象→但后验问题解决不来→换用putIfAbsent)
三、任务执行
  1. 线程中执行任务
       1.1.串行执行任务,一旦遇到耗时任务,CPU空闲,任务阻塞其他任务。
       1.2.并行执行任务,每个任务创建一个线程。
       1.3.显示创建线程执行每个任务的不足:线程有生命周期,创建线程好资源。如果请求过多,可能造成资源不足服务器崩溃。
  2. Executor框架:基于生产者消费者模型,子线程任务相当于消费者
       2.1. 略,简单的使用示例
       2.2. 执行策略:



       2.3. 线程池: 持有线程资源,可重用,提高响应。



       2.4. 生命周期:运行,关闭(不允许提交),已停止(任务完成后)。→优雅关闭线程池 shutdown(拒收)然后awaitTermination(定时终止,避免整体超时)最后finally里检验isTermination,如果还没关闭就调用shutdownNow尝试中断正在执行的任务。
       2.5. 延时任务:Timer由于单线程,任务之间可能由于耗时影响定时精确性。建议用任务调度线程池。
  3. 找出可利用的并行性
       3.1.举例页面渲染(文字+图片)的串行性劣势。读图片耗时
       3.2.介绍Callable和Future
       3.3.利用Future去优化页面渲染示例
       3.4. 相比同构任务的并行,异构任务并行存在局限。一个渲染文字一个渲染图片,渲染文字很快完成,渲染图片阻塞。那么其实对性能提高也不算高。
       3.5. CompletionService:Executor和阻塞队列介绍
       3.6.利用上述CompletionService实现,一旦有图片渲染完成的子线程就入队读取显示。而不是阻塞future的get等所有都渲染完成。
       3.7. future.get可以设置时限,比如获取广告超时执行默认操作推送默认广告。
四、取消与关闭
  1. 任务取消:操作过程中任务能被外部代码置为完成,称之为可取消的。




       1.1. 中断:用thread.sleep、thread.join、thread.wait、JDK1.5condition.await等操作都可进入阻塞状态,而线程在检查中断标示时如果发现中断标示为true,则会在这些阻塞方法调用处抛出InterruptedException异常。
       1.2. 中断策略:检查到中断时应该做退出,重试或者清理等操作。由执行语义决定。任务中断,线程中断都需要相应应对策略。提高程序健壮性和响应性
       1.3.响应中断:向上抛出异常或者处理之后恢复中断状态(再次Thread.interupt)确保调用链路的中断传递无误。
       1.4. 计时运行错误示范。没有响应中断。超时后当然正常,任务还没运行就可以中断。超时前完成,然后还执行中断代码。



       1.5. 用future得了
       1.6. 不可中断的阻塞,要知道阻塞原因,除了中断,还要手动处理阻塞原因,比如关闭socket等。

       1.7. 不是很理解对非标准取消的封装。

  2. 停止基于线程的服务:比如A线程创建B线程,B任务执行周期比A存活长,就要管理下B任务的生命周期。
       2.1.书本拿消费者生产者日志模型举例,一步步从最原始的阻塞,线程安全,一步步优化。PDF140
    托管给ExecutorService的几个关闭API
       2.3.毒丸对象
       2.4.示例 略
  3. 处理非正常线程停止:日志监测,RuntimeException处理
  4. JVM关闭:。。。。
    其实整章绕来绕去就是说Executor和Future带来多么便利。
五、线程池的使用:

1.任务间的隐形耦合:在Executor框架将任务的提交和执行解耦的前提下,我们修改和制定执行策略会更方便灵活。但并非所有的任务都适用所有的执行策略。有些任务就需要明确指定执行策略:①依赖性任务。②使用线程封闭机制的任务,任务隐形耦合③对响应时间敏感的④使用ThreadLocal的任务
   1.1线程饥饿死锁:如果任务依赖,一个任务将另一个任务提交到同一个线程工作队列并等待这个任务的结果,第二个任务又在等待第一个任务完成才可以继续。那么就会产生饥饿死锁。比如单线程Executor的页面渲染任务:本任务需要渲染页头和脚,本任务占有一个线程坑位,等下个任务完成。下个任务又阻塞在线程池里
   1.2 等待时间过长的任务:阻塞过久,不出现活跃性问题的话其性能也不佳。
2.设置线程池大小:P154自己看 公式。。。用得着这么精确吗=-=
3.配置ThreadPoolExecutor:挑选适合的实现。比如FIFO啊还是缓冲啊有界无界是否定时啊。
   3.1——3.5:P168PDF Executor详解 另外抽一章


END

流水账读书笔记完成。看过一遍,记笔记再过一遍,这样印象还是蛮深的。还差100页这本书就OK了√

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

推荐阅读更多精彩内容