主要的是讲多线程设计模式。。。其实有代码的,是java实现版的。。。。不过考虑到我说不定以后不怎么用java了,所以主要是把每个设计模式都列一个概述,思路总是有用的~~~所以觉得<Java多线程设计模式>这本书还是挺不错的~~~将每个多线程设计模式都用比较通俗易懂的语言来说明,而且代码都比较简单~~~~所以即使是不用java还是可以好好地理解一下多线程的魅力的~~~~

咔咔。。。写书的人总结得太好了。。。所以我只能上来他的笔记了。。。

1.Single Thread Execution Pattern

——能通过这座桥的,只有一个人

1
2
3
4
5
6
7
8
9
10
11
背景:
——多个线程共享一个实例
问题:
——若多个线程都擅自更改实例的状态,实例会丧失安全性
解决方式:
——首先,找出实例状态不稳定的范围(临界区间),并对临界区间加以防止同时执行的线程保持在只有一条的情况。
实现:
——java里面用synchronized
相关:
——当实例的状态不会改变时,为了提升throughput,可使用Immutable Pattern。
——想要将引用实例状态的线程与改变实例状态的线程拆开,以提高throughput时可使用Read-Write Lock Pattern

093641783.jpg

2.Immutable

——想破坏它也没办法

 

1
2
3
4
5
6
7
8
9
10
11
12
背景:
——多个线程共享一个实例,实例的状态不会改变
问题:
——使用Single Threaded Execution Pattern,会降低throughput
解决方式:
——当实例建立后状态就不会再变化时,就要停止使用Single Threaded Execution Pattern了。
——为了避免失误造成更改了实例的状态,故将类写成无法由线程更改。另外,删除实例里所有用来更新状态的方法(setter)。引用实例状态的方法(getter)就无妨
实现:
——java中使用private来隐藏字段,此外由于无法确保不可更改,因此还要使用final
关联:
——对多个线程进行共享互斥,可使用Single Threaded Execution Pattern。
——当修改用的线程数量比用来读取的线程数量多时,可考虑使用Read-Write Lock

 

094344815.jpg

3.Guard Suspension

——要等到我准备好喔

1
2
3
4
5
6
7
8
9
10
11
12
背景:
——多个线程共享一个实例
问题:
——若多个线程都擅自更改实例的状态,实例会丧失安全性
解决方式:
——当实例的状态不恰当时,就要求线程等待到适合的状态时。首先,以“警戒条件”来表示实例的“适当的状态”。并且在进行有安全性疑虑的操作前,都要检查是否警戒条件满足。如果警戒条件不成立,就要求线程等待到成立为止。
——使用Guarded Suspension Pattern,能以警戒条件限制方法的执行。不过,如果警戒条件一直不成立,线程会永远等待下去,会使程序丧失生命性
实现:
——java中,检验警戒条件使用while语句,而要让线程等待时则使用wait方法。并使用notify/notifyAll通知警戒条件的改变。检验、修改警戒条件时,会使用到Single Threaded Execution Pattern
相关:
——当警戒条件不成立时想要马上退出,就使用BalkingPattern
——Guarded Suspension Pattern中检验、更改警戒条件的部分,会使用到Single Threaded Execution Pattern

095302393.jpg

4.Balking

——不需要的话,就算了吧

1
2
3
4
5
6
7
8
9
10
11
背景:
——多个线程共享一个实例
问题:
——若多个线程都擅自更改实例的状态,实例会丧失安全性。可以一直等待安全的时机,又会使程序响应性降低。
解决方式:
——当实例的状态不适合时,就会中断掉处理的进行。首先,以“警戒条件”来表示实例的“适当的状态”,并且在进行有安全性疑虑的操作前,都要检查是否满足警戒条件。只有在警戒条件成立时,才会执行;如果警戒条件不成立,就直接中断(balk)执行,马上退出。
实现:
——java语言中,检验警戒条件时要使用if语句。当要balk时,可使用return退出,或使用throw抛出异常,检验、修改警戒条件时,会使用到Single Threaded Execution Pattern
关联:
——当想要等到警戒条件成立再执行时,可使用Guarded Suspension Pattern。
——Balking Pattern中检验、更改警戒条件的部分,会使用到Single Threaded Execution Pattern

095912625.jpg

5.Producer-Consumer

——我来做,你来用

1
2
3
4
5
6
7
8
9
10
背景:
——当要从某个线程(Producer参与者)将数据传给其他线程(Consumer参与者)时
问题:
——当Producer参与者与Consumer参与者处理的速度不同时,速度慢的会扯速度快的后退,而降低程序的throughput。另外,当Producer参与者要写入数据时,Consumer参与者若同时读取数据,数据会丧失安全性。
解决方式:
——在Producer参与者与Consumer参与者之间,加上重击用的Channel参与者。并让Channel参与者存放多条数据。这样就可以缓冲Producer参与者与Consumer参与者之间处理速度的差异。另外,只要在Channel参与者里进行共享互斥,数据就不会丧失安全性。于是throughput可以不降低,又可以在多个线程之间安全地传送数据。
相关:
——Channel参与者安全传递数据的部分,使用了Guarded Suspension Pattern。
——Future Pattern在传递返回值时,使用了Producer-Consumer Pattern
——Worker Pattern在传递请求时,使用了Producer-Consumer Pattern

100625969.jpg

6.Read-Write Lock

——大家想看就看吧,不过看得时候不能写喔

1
2
3
4
5
6
7
8
9
10
11
背景:
——多条线程共享一个实例,并会有参考实例状态的线程(Reader参与者),与会改变实例状体的线程(Writer参与者)
问题:
——若线程之间不进行共享互斥,会丧失安全性。但使用Single Threaded Execution Pattern会使程序throughput降低
解决方式:
——首先,将“控制Reader参与者的锁定”与“控制Writer参与者的锁定”分开,假如ReadWriteLock参与者,以提供两种不同的锁定。ReadWriteLock参与者会对“Writer参与者—Writer参与者”、“Reader参与者—Writer参与者”进行互斥控制。因为“Reader参与者-Reader参与者”不会发生冲突,故不会影响安全性,于是不进行共享互斥,这样可以在不影响安全性的前提下提高throughput
实现:
——java语言可以使用finally快避免忘记解除锁定。
相关:
——Read-Write Lock Pattern中,ReadWriteLock参与者进行共享互斥的地方,用到了Guarded Suspension Pattern
——完全没有Writer参与者的时候,可使用Immutable Pattern

101452260.jpg

7.Thread-Per-Message

——这个工作交给你了

1
2
3
4
5
6
7
8
9
10
背景:
——线程(Client参与者)要调用实例(Host参与者)的方法
问题:
——在方法的属性处理完之前,控制权不会从Host参与者退出。如果方法的处理属性花费时间,程序的响应性会降低
解决方式:
——在Host参与者里,启动新的线程。并将方法应该进行的工作,交割这个新的线程。这样Client参与者的线程就可以继续执行下一个操作了。这样做,不用更改Client参与者的程序代码,并能提高程序的响应性。
实现:
——java语言中,为了简化启动线程的程序,可使用匿名内部类
相关:
——想节省启动线程所花费的时间时,可以使用Worker Thread Pattern,想要将处理的结果返回给Client参与者时,可以使用Future Pattern

214909695.jpg

8.Worker Thread

——等到工作来,来了就工作

1
2
3
4
5
6
7
8
9
10
11
别名:
——Thread Pool;Background Thread
背景:
——线程(Client参与者)要调用实例(Host参与者)的方法
问题:
——如果方法的处理属性很花时间,程序的响应性会降低。为了提高响应性,而启动新的线程来处理方法时,启动线程所花的时间又会降低throughput。另外,当送出的请求太多时,会启动过多的线程,这会使承载量变差。
解决方法:
——首先,我们事先启动一些用来进行处理的线程(工人线程)。并将代表请求的实例传给工人线程。这样就不需要每次都重新启动新的线程了。
相关:
——想要获取工人线程的处理结果时,可以使用Future Pattern
想要将代表请求的实例传递给工人线程时,可以使用Producer-Consumer Pattern

214937593.jpg

9.Future

——先给您这张提货单

1
2
3
4
5
6
7
8
9
10
背景:
——线程(Client参与者)会将工作委托给其他线程,而Client参与者希望得到处理的结果
问题:
——将工作委托给别人时,如果又等待执行结果,会使响应性降低
解决方式:
——首先,建立一个与处理结果具有相同接口的Future参与者。在处理开始时,先把Future参与者当作返回值返回。处理的结果事后再设置给Future参与者。这样Clinet参与者就可以在适当的时机,通过Future参与者,获取(等待)处理的结果。
相关:
等待Client参与者的处理结果时,会使用Guarded Suspension Pattern
Future Pattern可用在Thread-Per-Message Pattern想要获取处理结果时。
Future Pattern可用在Worker Thread Pattern想要获取获取处理结果时。

215002100.jpg

10.Two-Phase Termination

——快把玩具收拾好,去睡觉吧

1
2
3
4
5
6
7
8
9
10
11
12
背景:
——想要结束运行中的程序
问题:
——从外部忽然结束掉线程,会丧失安全性。
解决方式:
——首先,适合进行终止的时机,还是要交给线程自己判断。所以,定义一个送出“终止请求”的方法用来结束线程:这个方法事实上只会将标识设置为“受到终止请求”而已,线程要在每个可以开始终止处理的地方自己检查这个标识。如果检查的结果为真,就开始进行终止处理。
实现:
——java语言中,不但要设置受到终止请求的标识,还要使用interrupt方法中断掉wait、sleep、join的等待状态。因为现场到wait、sleep、join抛出InterruptedException以后,就不是中断状态了,所以若是使用isInterrupted方法来检查终止请求,必须特别小心。
为了在执行时发生异常也能确实进行终止处理,所以要使用finally
相关:
——进行终止处理中时,为了禁止其他操作,可以使用Balking Pattern
为了确实进行终止处理,使用了Before/After Pattern

215032433.jpg

11.Thread-Specific Storage

——每个线程的保管箱

1
2
3
4
5
6
7
8
9
10
11
背景:
——想要将假定在单线程环境下运行的对象(TSObject参与者),在多线程的环境下使用。
问题:
——想要使用TSObject参与者并不简单。要将TSObject参与者改写成支持多线程,可能一部小心就丢掉安全性和生命性了。而且,TSObject参与者可能根本不能改写。而我们也不想改写使用TSObject参与者的对象(Client参与者)的程序代码,所以也不想修改TSObject参与者的接口。
解决方式:
——首先,建立一个与TSObject参与者具有相同接口的TSObjectProxy参与者,并建立TSObjectCollection参与者,管理“Client参与者->TSObject参与者”的对照关系
TSObjectProxy参与者会通过TSObjectCollection参与者,获取当前线程所对应的TSObject参与者,并将工作委托给TSObject参与者。Client参与者会拿TSObjectProxy参与者来代替TSObject参与者使用。
这样一来,每个TSObject参与者一定只会有特定的一个线程调用他,所以TSObject参与者不需要进行共享互斥,关于多线程的部分,都隐藏在TSObjectCollection参与者里了。另外,TSObject参与者的接口也不必修改。
不过,使用Thread-Specific Storage Pattern,等于是在程序里加上隐性的context,有程序的可读性可能变差的危险性。
实现:
——java语言中,使用java.lang.ThreadLocal类担任TSObjectCollection参与者

215055425.jpg

12.Active Object

——接受异步消息的主动对象

215107863.jpg

posted on 2015-04-13 22:27  小光zfg  阅读(830)  评论(0)    收藏  举报