现在的位置: 首页 > 综合 > 正文

第21章并发–java编程思想中文第四版-备忘笔记

2019年09月28日 ⁄ 综合 ⁄ 共 9483字 ⁄ 字号 评论关闭

 博学,审问、慎思、明辨、笃行
 读实用书时要提出的四个问题:
      (1)整体来说,这本书到底在谈些什么?big what?   并没有改变多少。
      (2)作者细部说了什么,怎么说的?  small what? how?
      (3)这本书说得有道理吗? 是全部有道理,还是部分有道理?why? 
           作者最终的目标,加上他建议的达成目标的方法--这要看你认为追求的是什么,以及什么才是最好的追求方法而定。
           在什么范围,什么条件下适用,在什么范围,什么条件下不适用。要知道它能做什么,它不能做什么
      (4)这本书跟你有什么关系? 理论联系实际
           赞同一本实用性的书,确实需要你采取行动。 照着作者希望你做的方式来行动。How
            行动:为达到某种目的而进行的活动。行动目标,行动方法,行动开始时间,结束时间,行动人,行动地点,行动方式。
            学车/骑自行车:理论就是要做题,实践:就是要上车操练。
            武术:主要是实践
            厨艺:理论就是看菜谱,实践:按菜谱买菜,配菜,实际炒菜,品尝,调整
            木匠/铁匠/建筑师:设计、实践,调整,在设计,在实践。
            美容美发:
            装修
            裁剪
            编程/软件工程/建筑工程在道的层次上很相近
        最忌讳:1 没有目标/提不出问题,没有时间和资源限制,自己太放松,要有适当的紧迫感和压力感
                2 重“看书”,轻“提问题,思考,讨论,实践” 有没有收获 关键看思考的深度及能否表达出来,实践出的东西是否是可见的。提高质量,追求卓越的态度
      1 什么是并发编程?什么多线程编程?有多线程架构、模式或惯例吗?
      2 每一节说了什么,怎么说的?
        (1)并发解决什么问题 (2)线程机制 (3)资源受限即共享资源 (4)线程之间协作 (5)线程停止 (6)新工具类 (7)仿真:生产者消费者模式、哲学家思考用餐模式、餐馆模式、分发工作(组装模式) (8)并发容器介绍List,Map,Queue (9) 活动对象等
      3 这一章说的有道理吗? 是全部有道理,还是部分有道理? why要用多线程编程?
      4 这一章跟自己有什么关系?
        赞同一本实用性的书,确实需要你采取行动。 照着作者希望你做的方式来行动。How
       如何编一些多线程HttpServer,或 TCPServer, SipServer,WebServer, 聊天室,接入,分发服务器
       异步用户操作界面、游戏等。
       实际操作(编写代码),做实验。
编程与开车,厨艺 有想通的地方,理论指导,以实践为主。
练习题:没做:(9)(17)(18)(19)(20)(21)(24)(25)(26)(28)(29) (31)(36)(38)(40)(41)(42) 作业
第21章 并发 2011-4-27 9:24 P686 
 21.1 并发的多面性(研习1次:掌握层次:熟练)     用并发解决的问题大体上可以分为:“速度” 和 “设计可管理性(并发提供一个重要的组织结构上的好处,便于设计编写松耦合的代码)”两种
    21.1.1 更快的执行(并发解决速度方面的问题):
          Web服务器,UI交互,并发异步
          CPU速度提高是以多核处理器的形式而不是更快的芯片的形式出现的。
          Erlang语言设计被设计为可以将并发任务彼此隔离的函数型语言。
    21.1.2 改进代码设计(并发解决设计可管理性方面的问题)线程能够常见更加松散耦合的设计。
          并发提供了一个重要的组织结构上的好处: 如仿真没有并发的支持是很难解决的。
          仿真:如计算机游戏或电影中计算机生成的动画。
          仿真通常设计许多交互式元素,每一个都有“其自己的想法”
          基于消息机制的架构
 21.2 基本的线程机制(研习1次:掌握层次:熟练)
       一个线程就是在进程中的一个单一的顺序控制流。因此,单个进程可以拥有多个并发执行的任务,但是你的程序使得每个任务都好像有自己的CPU一样。对于单核CPU其底层机制是切分CPU时间,对于多核CPU其底层机制是将多个任务并行的分配给各个CPU.
    21.2.1 定义任务
    21.2.2 Thread类/Runnable接口
           run() {} 没有返回值
    21.2.3 使用Executor P692
    21.2.4 从任务中产生返回值 Future
           ExecutorService ex = Executors.newSingleThreadExecutor();
              List<Future<T>> = ex.invokeAll(tasks);
          <T> T           = ex.invokeAny(tasks);
          <T> Future<T>   = ex.submit(task);
    21.2.5 休眠 P696
    21.2.6 优先级
           1)什么是优先级?首先要分级别,然后不同的级别有不同的优先处理顺序。
           2)为什么要有优先级?没有优先级会怎样?现实生活中存在优先级吗? 能举几个例子吗?优先级解决什么问题?
              根据重要性、紧急性等因素,将不同的事情,分不同的优先级,如重要的事情优先处理,不重要的事情放后处理。避免胡子眉毛一把抓,从而能够保证抓住生活或工作中的主要矛盾/重点。
                  举例:同在马路上行驶的汽车,当警车执行紧急重要任务时,可以优先通行。(救护车、军车、工程抢险车等)
              政治上,不同权利的人,有不同的优先权。
                  国家元首下飞机优先走在前面,其他人按职位等因私依次从后面走出来。(政治上、经济上、身体条件等。)
           3)怎样在编程中实现优先级:做个有优先级的实验,感性认识一下。
                  云台控制优先权、一个线程/进程可以独立活动的实体,(类似人,如多线程类似于多个人,一个线程类似于一个人等)
                  线程优先执行的权利:
    21.2.7 让步
           1)什么叫让步?将自己占有的资源让给别人或别的实体。
           2)为什么要让步?不让步会有什么问题? CPU/内存/IO/网络都是资源。
              当自己用完资源后,可以让其他人来用这个资源。
              如果你一直占着这个资源,别人想用这个资源就需要等待,直到资源可用为止。
           3)怎样让步?编程如何实现?
    21.2.8 后台线程
           1)什么是线程?什么是后台线程?既然有后台线程,是否有非后台线程或前台线程呢?
               主线程:JVM调用程序main()所产生的线程,前台线程。
              当前线程:这个是容易混淆的概念。一般指通过Thread.currentThread()来获取的进程。
              后台线程:指为其他线程提供服务的线程,也称为守护线程。JVM的垃圾回收线程就是一个后台线程。
              前台线程:是指接受后台线程服务的线程,其实前台后台线程是联系在一起,就像傀儡和幕后操纵者一样的关系。傀儡是前台线程、幕后操纵者是后台线程。由前台线程创建的线程默认也是前台线程。可以通过isDaemon()和setDaemon()方法来判断和设置一个线程是否为后台线程。
           2)为什么要有后台线程?没有后台线程会怎样呢?后台线程的存在的意义?后台线程可以做什么,不可以做什么?
              台前幕后
              一个JVM进程启动后里面有几个线程:http://littlefermat.blog.163.com/blog/static/59771167200931243741618/
           3)怎样使用后台线程? 试着做个试验,感受一下后台线程。
              练习9 需要重新看一下
    21.2.9 编码的变体(研习1次:掌握层次:会)
           1 继承Thread
           2 自管理的Runnable
           3 有名字的内部类方式创建线程
           4 匿名方式创建线程
           5 有名字的Runnable实现
           6 匿名的Runnable实现
           7 在方法中创建线程
           8 传统方式:think in java使用最多的一种形式
    21.2.10 术语/概念
         Task:描述将要执行的工作时使用术语“任务”。
         线程:在引用到驱动任务的具体机制时,才使用“线程”。
    21.2.11 加入一个线程CyclicBarrier比join()更适合的选择。
         1) 什么是Join?  
             一个线程可以在其他线程之上调用join()方法,其效果是等待一段时间直到第二个线程结束才继续执行。
             如果某个线程在另一个线程t上调用t.join(),此线程将被挂起,直到目标线程t结束才恢复(即t.isAlive()返回为假)。
             对join()方法的调用可以被中断,做法是在调用线程上调用interrupt()方法,这时需要用到try-catch子句。
         2)  为什么要用Join?
         3) 怎样使用Join? 什么情况下使用,什么情况下不适宜使用?
             尝试做个实验,感受一下。
    21.2.12 创建有相应的用户界面
           至少有有两个用户线程:一个调用main函数的线程,另一个是main函数调用生成的子线程。
    21.2.13 线程组: 忽略线程组,不建议使用
    21.2.14 捕获异常
            如何捕获异常,避免其向外传播到控制台。
 21.3 共享受限资源 P710 (研习1次:掌握层次:会)
      1)什么是共享(受限)资源?
           两个或多个是身体试图同时使用同一个资源,就出现共享受限资源
      2)为什么会出现共享资源?能够避免吗?
           现实生活中共享资源的例子:SVN,VSS 两个人同时在一个地方停车,两个人同时说话,两个人同时竞争占用资源。
      3)怎样处理共享资源呢?
      4)什么时间处理?完成?
      5)谁应该知道?
      6)在哪里?
      7)多少资源?
    21.3.1 不正确地访问资源
         在java中,递增递减(i++ ++i i-- --i)不是原子性的操作。
    21.3.2 解决共享资源竞争
           如何解决共享资源竞争?
           对于并发工作,需要某种方式(哪种方式?synchronized/Collections.synchronizedXXX()/lock/...)来防止两个任务同时访问相同的资源?
           防止这种冲突的方法就是当资源被一个任务使用时在其上加锁。第一个访问某项资源的任务必须锁定这项资源,使其他任务在其被解锁之前,就无法访问它,
           而在其被解锁之时,另一个任务就可以锁定并使用它,依次类推。
             基本上所有的并发模式在解决线程冲突问题的时候,都是采用序列化访问共享资源的方案。这意味着在给定时刻只允许一个任务访问共享资源。
          通常这是通过在代码前面加上一条锁语句来实现的, 这就使得在一段时间内只有一个任务可以运行这段代码。因为锁语句产生了一种互相排斥的效果,所以这种机制常常称为互斥量(mutex). 使用浴室的例子
          什么时候同步呢?
           如果你正在写一个变量,它可能接下来将被另一个线程读取,
          或者正在读取一个上一次已经被另一个线程写过的变量,那么你必须使用同步,
          并且,读写线程都必须用相同的监视器锁同步。
       使用显式的Lock对象:
          Lock对象必须被显式地创建、锁定和释放。
          注意:return语句必须在lock范围内
    21.3.3 原子性与易变性 P716 (研习1次:掌握层次:半会)
         什么是原子性?用javap -c 类名 通过反编译的字节码看一看到如果get和put还有一些其他的指令,这个操作就不是原则性的。
         什么是可视性?volatile 易变性?
            如果将一个域定义为volatile,那么它就会告送编译器不要执行任何移除读取和写入操作的优化,这些操作的目的是用线程中的局部变量维护这个域的精确同步。实际上,读取和写入都是直接针对内存的,而却没有被缓存。

    21.3.4 原子类P720 (研习1次:掌握层次:半会)
      (1)什么是原子类?(整体来说,这本书到底在谈些什么?big what?   并没有改变多少。)
           java.util.concurrent.atomic包下的类
      (2)作者具体在原子类说了什么,怎么说的?(作者细部说了什么,怎么说的?  small what? how?)
          
      (3)在原子类这一节,作者的目标,方法?( 这本书说得有道理吗? 是全部有道理,还是部分有道理?why? 
                                                作者最终的目标,加上他建议的达成目标的方法--这要看你认为追求的是什么,以及什么才是最好的追求方法而定。)
      (4)我赞同作者对原子类的观点吗?采取行动了吗?(这本书跟你有什么关系? 理论联系实际)
                                                      赞同一本实用性的书之后,确实需要你采取行动。 照着作者希望你做的方式来行动。How
    21.3.5 临界区
       临界区:只防止多个线程同时访问方法内部的部分代码而不是防止访问整个方法,通过这种方式分离出来的代码段被称为临界区(critical section),它也使用synchronized关键字建立。这里,synchronized被用来指定某个对象,此对象的锁被用来对花括号内的代码进行同步控制。也被称为同步控制块。
       在临界区上加锁,比直接在临界区外的方法上加锁有更好的性能,同时出问题的概率也高一些。
    21.3.6 在其他对象上同步
    21.3.7 线程本地存储
       防止任务在共享资源上产生冲突的第二种方式是根除对变量的共享(第一种方式是锁机制,保持顺序访问共享资源)。线程本地存储是一种自动化机制,可以为使用相同变量的每个不同的线程都创建不同的存储。
    21.3.8
 21.4 终结任务 (研习1次:掌握层次:半会)
     21.4.1 装饰性花园
     21.4.2 在阻塞时终结
??  21.4.3 中断 (研习1次:掌握层次:半会)
??  21.4.4 检查中断 (研习1次:掌握层次:半会)
      当任务协作时,关键问题是这些任务之间的握手。为了实现这种握手,我们使用了相同的基础特性:互斥。在这种情况下,互斥能够确保只有一个任务可以相应某个信号,这样就可以根除任何可能的竞争条件。
      在互斥之上,为线程增加了一种途径,可以将其自身挂起,直到某些外部条件发生变化,表示是时候让这个任务向前开动了为止。
      任务间的握手问题可以通过Object的方法wait()和notify()来安全地实现。Java SE5的并发类库还提供了具体有await()和signal()方法的Condition对象。
 21.5 线程之间的协作 P739 (研习1次:掌握层次:会)
     21.5.1 wait()与notifyAll() P743
      调用sleep()和yield()的时候锁并没有被释放,但调用wait(0时,线程的执行被挂起,对象上的锁被释放。
      只能在同步控制方法或同步控制块里调用wait()、notify()和notifyAll()(因为不用所操作,所以sleep()可以在非同步控制方法里调用)。
(未看) 21.5.2 notify()与notifyAll() P745 (研习1次:掌握层次:未看)
     21.5.3 生产者与消费者 P749
     21.5.4 生产者-消费者与队列
     21.5.5 任务间使用管道进行输入/输出 P753
     21.6 死锁 P758
 21.7 新类库中的构件(研习1次:掌握层次:会)
     21.7.1 CountDownLatch P758
     21.7.2 CyclicBarrier 类似于join()
     21.7.3 DelayQueue  P762
            DelayQueue是什么?Delayed元素的一个无界阻塞队列,只有在延迟期满时才能从中提取元素。有序,不能存储null
            DelayQueue为什么这样设计?
            DelayQueue在什么情况下适用?什么情况不适合? 如何适用?如何使用?
     21.7.4 PriorityBlockingQueue
     21.7.5 使用ScheduledExecutor的温室控制器 P768
     21.7.6 Semaphore:信号量支持:二进制信号量,非二进制信号量 P769
     21.7.7 Exchanger是在两个任务之间交换对象的栅栏。
            Exchanger应用场景:一个任务在创建对象,这些对象的生产代价很高贵,而另一个任务在消费这些对象。通过这种方式,可以有更多的对象在被创建的同时被消费。
 21.8 仿真 P773(研习1次:掌握层次:半会,需要重点研究)
     21.8.1 银行出纳员仿真: 是什么?应用模式。为什么这样? 很好的处理这种场景 怎样实现呢?
      经典场景:对象随机地出现,并且要求由数量有限的服务器提供随机数量的服务时间。 通过构建仿真可以确定理想的服务器数量。
     21.8.2 饭店仿真 P777
      主要技术:使用队列在任务间通信所带来的管理复杂度管理。这个单项技术通过反转控制极大地简化了并发编程的过程:任务没有直接地相互干涉,而是经由队列互相发送对象。接受任务将处理对象,将其当作一个消息来对待,而不是向它发送消息。
     21.8.3 分发工作
 21.9 性能调优P784(研习1次:掌握层次:会)
     21.9.1 比较各种互斥技术P784
            性能从好到差:Atomic -> Lock -> BaseLine -> synchronized
            安全的做法:以传动的互斥方式入手,只有性能方面的需求能够明确指示时,再替换为Atomic.
     21.9.2 免锁容器P790
               早期像Vector和Hashtable容器具有许多synchronized方法。用于非多线程的应用程序中时,便会导致不可接受的开销。
               Java1.2中,新的容器类库是不同步的,并且Collections类提供了各种static的同步的装饰方法,从而来同步不同类型的容器。
               Java SE5添加的新容器,通过更灵活的技术来消除加锁,从而提高性能。
               这些免锁容器背后的通用策略是:对容器的修改可以与读取操作同时发生,只要读取者只能看到完成修改的结果即可。修改是在容器数据
               结构的某个部分的一个单独的副本(有时是整个数据结构的副本)上执行的,并且这个副本在修改过程中是不可视的。只有当修改完成时,
               被修改的结构才会自动地与主数据结构进行交换,之后读取者就可以看到这个修改了。
              *在CopyOnWriteArrayList中,写入将导致创建整个底层数组的副本,而源数组将保留在原地,使得复制的数组在被修改时,读取操作可以安全地执行。
               当修改完成时,一个原子性的操作将把新的数组换入,使得新的读取操作可以看到这个新的修改。CopyOnWriteArrayList的好处之一是当多个迭代器同时
               遍历和修改这个列表时,不会抛出ConcurrentModificationException
           *乐观锁 :CopyOnWriteArrayList     可以替代Collections.synchronizedList(new ArrayList());
           *比较各种Map实现:ConcurrentHashMap 可以替代Collections.synchronizedMap(new HashMap());
     21.9.3 乐观加锁P796
            Atomic对象
     21.9.4 ReadWriteLock P797
            ReadWriteLock对向数据结构相对不频繁地写入,但是有多个任务要经常读取这个数据结构的这类情况进行了优化。ReadWriteLock使得你可以同时有多个
            读取者,只要它们都不试图写入即可。如果写锁已经被其他任务持有,那么任何读取者都不能访问,直至这个写锁被释放为止。
 21.10 活动对象 P799(研习1次:掌握层次:半会)
       活动对象、行动者或代理的信息,C.A.R. Hoare的通信顺序进程理论(Theory of Communicating Sequential Processes, CSP)
 21.11 总结 P802(研习1次:掌握层次:会)
       如果线程问题大而复杂,应该考虑使用像Erlang这样的针对并发的语言。
 21.11.1 进阶读物(研习1次:掌握层次:会)
       《java Concurrency in Practice》
       《Concurrent Programming in java Second Edition》Doug Lea
       《The java language Specification, Third Edition》(第17章)

后续需要重点研究:
 掌握层次:在不会、半会、会层次的
 重点看练习题:(9)(17)(18)(19)(20)(21)(24)(25)(26)(28)(29) (31)(36)(38)(40)(41)(42) 作业
 编程是技能,根据问题,思考,搜集资料,动手实验等。

抱歉!评论已关闭.