Java 多线程设计模式
基础知识:
java内存模型:主存储器,工作存储器
主存储器就是实例位置所在区域,所有的实例都位于主存储器内。尤其实例所拥有的字段即位于主存储器内的区域。主存储器为所有的线程所共有。
工作存储器为各个线程所拥有的作业区,,所有的线程都用其专用的工作存储器。在工作存储器内,存在有主存储器中必要部分的拷贝,称之为工作拷贝(workingcopy)
主存储器好比是大家都看得到的黑板;而工作存储器就像每个人的笔记本
synchronized的两项功能
synchronized原意为“同时取得”,这里的“同步”分为“线程的同步”与“内存的同步”两种。
Volatile(易变的反复无常的)的两项功能
1,
2,
Volatile仅进行内存的同步,当线程欲引用volatile字段的值时,通常都会发生从主存储器到工作存储器到工作存储器的拷贝操作。而相反的,将值指定给写着volatile的字段后,工作存储器的内容通常便会映像到主存储器。
指定给long和double型的操作不保证会以atomic的方式来进行。但如果是long或double型的字段只要有写着volatile,就可以用atomic的方式来进行制定工作。
并行与并发
1,
2,如果是在有一个以上中央处理器的计算机上跑得java执行处理系统,则线程的操作可能是并行(parallel)而非并发。当一个以上的线程并行操作时,就可以真正同时进行一个以上的处理。
Ch1:Single Threaded ExecutionPattern(每次通过这座桥的人只能有一个)
当一个资源会被多个线程访问时,需要使用synchronized
注意需要保护的区间
Ch2:Immutable(永恒的)Pattern(想破坏它也没有办法)
使用时机:
当实例产生后,实例的状态就不再变化是首要条件。
所谓实例的状态,则是由字段的值所确定的,所以将字段设置成final,并且不要定义setter方法则是重点所在,然而这并不是足够的。
Immutable类里面的字段的引用也不能被外面的类持有,考虑到各种情况,传参数等。
Ch3:Guarded Suspension(暂停)Pattern(要等到我准备好哦)
当现在不适合马上执行某个操作时,就要求想要执行该线程等待,这就是Guarded SuspensionPattern.(也叫Guarded Wait,Spin Lock等)
这个模式中,有一个具有状态的对象。而这个对象只有在自己的状态合适的时候才会让线程进行目的处理。于是,首先我们以“警戒条件”来表示对象合适的状态。并在目的处理前,测试现在使用满足警戒条件。只有在警戒条件满足时,才会进行目的处理;而警戒条件不成立时,就会等待到成立为止。是有条件的Synchronized
单线程中这种情况会用if处理,所以Guarded Suspension Pattern也可以理解为多线程版本的if
Ch4:Balking(犹豫)Pattern(不需要的话,就算了吧)
Java语言中,使用if语句来测试警戒条件。Balk的时候,可以使用return方法退出,或者是使用throw抛出异常。警戒条件的测试,要使用synchronized放进临界区间里。
Ch5:Producer-ConsumerPattern(你来做,我来用)
Producer(生产者)是指产生数据的线程,Consumer(消费者)是指使用数据的线程
Ch6:Read-Write LockPattern(大家想看就看吧,不过看的时候不能写哦)
Read-Write LockPattern将读取与写入分开来处理。在读取数据之前,必须获取用来读取得锁定。而要写入的时候,则必须获取用来写入的锁定。
Ch7:Thread-Per-MessagePattern(这个工作就交给你了)
Thread per message 解释过来就是“每个消息一个线程”。Message在这里可以看作是“命令”或“请求”的意思。对每个命令或请求,分配一个线程,有着个线程执行工作,这就是Thread-Per-MessagePattern。
进程和线程
1,进程和线程最大的差异在于内存能否共享。
通常每个进程所拥有的内存空间是各自独立的。进程不能擅自读取,改写其他进程的内存空间。因为进程的内存空间是相互独立的,所以进程无需担心被其他进程破坏的危险。
而线程则是共享内存的。
2,
进程切换需要存储和保留的信息比较多,所以切换需要花费一些时间。然而线程需要管理的context信息比进程要少得多,所以一般而言县城context-switch比进程的context-switch要快的多。
Ch8:Worker ThreadPattern(等到工作来,来了就工作)
Worker是工人的意思,Worker Thread Pattern中,工人线程(workerthread)会依次抓一件工作来处理。当没有工作可做时,工人线程会停下来等待新的工作过来。WorkerThread也有人称为Background Thread。
Swing:事件监听机制,eventDispatcher
使用Thread-Per-MessagePattern将“送出工作请求的线程”与“执行工作的线程”分离开来,可以提高程序的响应性。
但是,每次送出工作请求时,都为这个工作启动新的线程也很浪费。
所以我们事先启动来执行工作的线程(工人线程)备用。并使用Producer-ConsumerPattern,将表示工作内容的实例传递给工人线程。
这么一来,工人线程会负责执行工作,就不需要一直启动新的线程了。
通过这个模式我们将“invocation与execution的分离”,也就是方法的启动与执行分离。在不同在线程上分别启动和执行方法。
Ch9:Future Pattern(献给您这张提货单)
Future是“未来”,“期货”的意思。
总结:
这种时候就要使用FuturePattern。首先我们建立一个与处理结果具有相同接口(API)的Future参与者。接着,在开始处理时,先把Future参与者当作返回值返回。直到其他线程处理完以后,才将真正的结果设置给Future参与者。Client参与者可以通过Future参与者得到处理的结果。
使用这个Pattern,可使响应性不降低,并得到想要的处理结果。
Ch10:Two-Phase TerminationPattern(快把玩具收好,去睡觉吧)
我们将线程进行平常的处理的状态称为【作业中】。当希望结束这个线程时,则送出“终止请求”。接着这个线程,并不会马上结束,而会开始进行必要的刷新工作。这个状态称为【终止处理中】。从【作业中】改变成【终止处理中】是第一阶段。
Ch11:Thread-Specific StoragePattern(每个线程的保管箱)
Thread-Specific StoragePattern是只有一个门,但内部会对每个线程提供特有的存储空间的Pattern。
关于java.lang.ThreadLocal类
Actor-based 与Task-based(注重主体与注重客体)
Actor-based开发方式中,代表线程的实例,会拥有进行工作所需的信息(context,状态)。这样一来,可以降低线程之间需要传递的信息。每个线程会使用其他线程所传来的信息进行处理,改变自己的内部状态。这种线程通常称为actor(操作者)
Task-based的开发方式,就是不将信息(context,状态)放到线程里。而是把信息放在线程之间所传递的实例里。并不是只有数据,包括用来执行工作的方法,也放在这里实例里。这种线程间传递的实例称为“消息”,“请求”或“命令”。在这里我们又称它为task(任务)。任务内含有足够的信息,所以任务可以由任何线程来进行。合格开发方式,可使巨大的任务能在轻巧的线程之间往来。
Ch12:Active ObjectPattern(接收异步消息的主动对象)