多线程设计模式

java内存模型:主存储器,工作存储器

         主存储器就是实例位置所在区域,所有的实例都位于主存储器内。尤其实例所拥有的字段即位于主存储器内的区域。主存储器为所有的线程所共有。

         工作存储器为各个线程所拥有的作业区,,所有的线程都用其专用的工作存储器。在工作存储器内,存在有主存储器中必要部分的拷贝,称之为工作拷贝(working copy)

        主存储器好比是大家都看得到的黑板;而工作存储器就像每个人的笔记本

 

synchronized的两项功能

synchronized原意为“同时取得”,这里的“同步”分为“线程的同步”与“内存的同步”两种。

         使用synchronized来制作临界区间(critical section)时,该区间仅能提供一个线程进行操作。在一个线程作业期间,其他的线程必须在critical section 的入口处等待。由synchronized所规定的范围,来控制线程的操作。此为线程的同步。

         内存同步就是工作内存区和主存储器的同步,这个一般会在“欲进入”与“欲退出”synchronized时进行,如果是“正在synchronized 内”或“正在synchronized外”时,便不保证一定会进行工作存储器与主存储器之间的同步。执行处理器或许会根据这个来进行最佳化。

 

Volatile(易变的反复无常的)的两项功能

         1、 进行内存的同步

         2、已atomic的方式来进行long,double的制定

          Volatile仅进行内存的同步,当线程欲引用volatile字段的值时,通常都会发生从主存储器到工作存储器到工作存储器的拷贝操作。而相反的,将值指定给写着volatile的字段后,工作存储器的内容通常便会映像到主存储器。

          指定给long和double型的操作不保证会以atomic的方式来进行。但如果是long或double型的字段只要有写着volatile,就可以用atomic的方式来进行制定工作。

 

并行与并发

         1、 程序的处理是不断切换有操作的线程,这种操作,就称为并发(concurrent)

          2、如果是在有一个以上中央处理器的计算机上跑得java执行处理系统,则线程的操作可能是并行(parallel)而非并发。当一个以上的线程并行操作时,就可以真正同时进行一个以上的处理。

 

 

Single Threaded Execution Pattern(每次通过这座桥的人只能有一个)

        当一个资源会被多个线程访问时,需要使用synchronized

        注意需要保护的区间

 

Immutable(永恒的) Pattern(想破坏它也没有办法)

        当实例产生后,实例的状态就不再变化是首要条件。

         所谓实例的状态,则是由字段的值所确定的,所以将字段设置成final,并且不要定义setter方法则是重点所在,然而这并不是足够的。

         Immutable类里面的字段的引用也不能被外面的类持有,考虑到各种情况,传参数等。

 

Guarded Suspension(暂停) Pattern(要等到我准备好哦)

        当现在不适合马上执行某个操作时,就要求想要执行该线程等待,这就是Guarded Suspension Pattern.(也叫Guarded Wait,Spin Lock等)

         这个模式中,有一个具有状态的对象。而这个对象只有在自己的状态合适的时候才会让线程进行目的处理。于是,首先我们以“警戒条件”来表示对象合适的状态。 并在目的处理前,测试现在使用满足警戒条件。只有在警戒条件满足时,才会进行目的处理;而警戒条件不成立时,就会等待到成立为止。是有条件的 Synchronized

        单线程中这种情况会用if处理,所以Guarded Suspension Pattern也可以理解为多线程版本的if

 

 

Balking(犹豫) Pattern(不需要的话,就算了吧)

 

         当现在不适合进行这个操作,或者是没有必要进行这个操作时,就直接放弃进行这个操作而回去,这就是Balking Pattern。这个模式和Guarded Suspension Pattern一样需要警戒条件,不过在这里不符合条件的话,不是去等待而是直接放弃。

 

Java语言中,使用if语句来测试警戒条件。Balk的时候,可以使用return方法退出,或者是使用throw抛出异常。警戒条件的测试,要使用synchronized放进临界区间里。

 

Producer-Consumer Pattern(你来做,我来用)

Producer(生产者)是指产生数据的线程,Consumer(消费者)是指使用数据的线程

         生产者必须将数据安全的交给消费者。虽然只是这样的问题,但当生产者与消费者在不同线程上运行时,两者的俄处理速度差将是最大的问题。当消费者要取数据时生产者还没建立出数据,或是生产者建立出数据时消费者的恶状态还没有办法接收数据等。

         Producer-Consumer Pattern是在生产者和消费者之间加入一个“桥梁参与者”。来缓冲线程之间的处理速度差。(有可能有多个生产者和多个消费者)

 

 

Read-Write Lock Pattern(大家想看就看吧,不过看的时候不能写哦)

         Read-Write Lock Pattern将读取与写入分开来处理。在读取数据之前,必须获取用来读取得锁定。而要写入的时候,则必须获取用来写入的锁定。

         因为进行读取时,实例的状态不会改变,所以,就算有多个线程在同时读取也没有关系。但,有人在读取的时候,不可以做写入操作。

         写入的时候,实例的状态就会改变。于是,当有一个线程在写入的时候,其他线程就不可以有进行读取或写入。

 

 

Thread-Per-Message Pattern(这个工作就交给你了)

         Thread per message 解释过来就是“每个消息一个线程”。Message 在这里可以看作是“命令”或“请求”的意思。对每个命令或请求,分配一个线程,有着个线程执行工作,这就是Thread-Per-Message Pattern。

         使用Thread-Per-Message Pattern 时,“委托消息的一端”与“执行消息的一端” 会是不同的线程。也就像是委托消息的线程,对执行消息的线程说“这个工作就交给你了”。

进程和线程

         1、进程和线程最大的差异在于内存能否共享。

         通常每个进程所拥有的内存空间是各自独立的。进程不能擅自读取,改写其他进程的内存空间。因为进程的内存空间是相互独立的,所以进程无需担心被其他进程破坏的危险。

而线程则是共享内存的。

         2、 进程和线程另一个差异,在于context-switch的频繁程度。

         进程切换需要存储和保留的信息比较多,所以切换需要花费一些时间。然而线程需要管理的context信息比进程要少得多,所以一般而言县城context-switch比进程的context-switch要快的多。

 

Worker Thread Pattern(等到工作来,来了就工作)

        Worker是工人的意思,Worker Thread Pattern中,工人线程(worker thread)会依次抓一件工作来处理。当没有工作可做时,工人线程会停下来等待新的工作过来。Worker Thread也有人称为Background Thread。

        Swing:事件监听机制,eventDispatcher

        使用Thread-Per-Message Pattern将“送出工作请求的线程”与“执行工作的线程”分离开来,可以提高程序的响应性。

        但是,每次送出工作请求时,都为这个工作启动新的线程也很浪费。

所以我们事先启动来执行工作的线程(工人线程)备用。并使用Producer-Consumer Pattern,将表示工作内容的实例传递给工人线程。

        这么一来,工人线程会负责执行工作,就不需要一直启动新的线程了。

         通过这个模式我们将“invocation与execution的分离”,也就是方法的启动与执行分离。在不同在线程上分别启动和执行方法。

 

Future Pattern(献给您这张提货单)

        Future是“未来”,“期货”的意思。

         假设有一个执行起来需要花一些时间的方法,我们就不要等待执行结果出来了,而获取一张替的“提货单”。因为获取提货单不需要花时间,这时这个“提货单”就是Future参与者。

         获取Future参与者的线程,会在事后再去获取执行结果。就好像那提货单去领取蛋糕一样,如果已经有执行结果了,就可以马上拿到数据。如果执行结果还没好,则继续等待到执行结果出现为止。

         上一章实现了启动方法与执行方法的分离,而Future则可以将“准备方法的返回值”与“使用方法的返回值”分离。而且可以在不同的线程上处理。

         将调用方法的一连串动作,像慢动作播放般逐一拆解。接着将分解出来的每个肚子的操作(启动,执行,准备返回值,使用返回值),尝试分配给不同的线程。我们把多线程当作道具使用,就是在做这些事情。

 

总结:

         Thread-Per-Message-Pattern是将花费时间的工作交给别的线程,以提高程序的响应性。不过,如果我们需要得到别的线程所处理的结果时,就行不通了。

         若同步执行需要花一些时间的操作,会使程序响应性降低。但是,如果异步的开始执行,却无法在第一时间得知结果。

        这种时候就要使用Future Pattern。首先我们建立一个与处理结果具有相同接口(API)的Future参与者。接着,在开始处理时,先把Future参与者当作返回值返回。 直到其他线程处理完以后,才将真正的结果设置给Future参与者。Client参与者可以通过Future参与者得到处理的结果。

使用这个Pattern,可使响应性不降低,并得到想要的处理结果。

 

Two-Phase Termination Pattern(快把玩具收好,去睡觉吧)

         我们将线程进行平常的处理的状态称为【作业中】。当希望结束这个线程时,则送出“终止请求”。接着这个线程,并不会马上结束,而会开始进行必要的刷新工作。这个状态称为【终止处理中】。从【作业中】改变成【终止处理中】是第一阶段。

         【终止处理中】的状态时,不会进行平常的操作。虽然线程还在运行,但进行得斯终止处理。直到终止处理结束后,才真正结束线程。【终止处理中】的操作结束,是第二阶段。

         先从【作业中】进入【终止处理中】状态,才真正结束掉线程。这就是Two-Phase Termination Pattern。

         主要考虑大关键因素如下:安全的结束(安全性)、一定会进行终止处理(生明性)收到终止请求后,要尽快开始终止处理(响应性)

 

Thread-Specific Storage Pattern(每个线程的保管箱)

         Thread-Specific Storage Pattern是只有一个门,但内部会对每个线程提供特有的存储空间的Pattern。

关于java.lang.ThreadLocal

         java.lang.ThreadLocal的实例可以想象成一种集合架构(collection)。也就是说ThreadLocal的实例只有一个,管理多个对象。为线程提供许多保管箱。

         ThreadLocal拥有set和get方法。线程直接调用set方法就可以把需要存的东西存到ThreadLocal中,直接以线程本身的 Thread.currentThread()的值作为键值(系统默认)get的时候默认找对应线程的箱子中的东西,如果没有则返回null。

Actor-based 与Task-based(注重主体与注重客体)

         所谓Actor-based,用一句话说就是“偏重于线程”的开发方式。

Actor-based开发方式中,代表线程的实例,会拥有进行工作所需的信息(context,状态)。这样一来,可以降低线程之间需要传递的信息。每个线程会使用其他线程所传来的信息进行处理,改变自己的内部状态。这种线程通常称为actor(操作者)

         所谓Task-based,用一句话说,就是“偏重于工作”的开发方式。

Task-based的开发方式,就是不将信息(context,状态)放到线程里。而是把信息放在线程之间所传递的实例里。并不是只有数据,包括用来执 行工作的方法,也放在这里实例里。这种线程间传递的实例称为“消息”,“请求”或“命令”。在这里我们又称它为task(任务)。任务内含有足够的信息, 所以任务可以由任何线程来进行。合格开发方式,可使巨大的任务能在轻巧的线程之间往来。

         这种开放方式最典型的就是Worker Thread Pattern

 

Active Object Pattern(接收异步消息的主动对象)

         Active是“主动的”的意思,所以active object 是“主动对象”的意思。“主动的”一般是指“自己拥有独立的线程”的意思。所以java的java.lang.Thread类的实例,也是一种主动对象.

         不过,Active Object Pattern里出现的“主动对象”,并不只是“自己拥有独立的线程”而已。他还具有可以从外部接受异步消息,并能配合需要返回处理结果的特征。

         使用Active Object Pattern组织成“主动对象”,就很像一群人集合在一起构成法人一样。虽然使用没有提供异步消息的java语言,但是使用Active Object Pattern,就可以组织出能够处理异步消息的“主动对象”。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值