Java 多线程编程中的设计模式 开篇

<放水开始>
什么是模式?有太多的介绍模式的书,似乎用不着我再来给模式下一个定义.正如什么是道?
除了老子有权解释,其他任何试图向别人解释什么是道的人,其实根本就不懂道!更别说什么
初论,再论,三论,x论,末流之论也.你不论说明你还懂一些,如果你论,说明你根本就不懂道.
因为老子已经说得非常清楚:道可道,非常道!

那么我们是否已经陷入了不可知论?非也.不可道之道,是真正的大道.大道虽然无形,但每个人对它
的感受是有形有,我们在向别人说明时其实说的是你"自己对道的感觉",无论你如何论道你的感觉都
不代表真正的"道".

因为每个人的感觉不同,所以尽管有很多人介绍过模式,我从中学习了很多我不知道的知识,但我有
我的感受,我也有其它人不知道的知识,或者张狂一点说比某些人高明一些的知识,所以我要写我自
己的模式系列.以我的观点而言:
<放水暂停>


模式就是在某种特定环境中,经常多次发生的问题的通用解决方案.

环境,或说背景,用Java术语说是Context.
问题里面还包含可以解决问题的外力条件(force),多个外力条件常常表现为"鱼与熊掌"的关系.

尽管目前真正介绍模式的书并不多,但在可见的介绍模式的书中,可以看到多数就是一本
模式词典.设计模式中真正的知识在于[模式语言].

模式语言是模式的集合,集合的意思是集而合之,不是聚合.
把多个相关的,相互作用的模式集而合之,并清楚地描述它们之间的关系.就是模式语言.

模式是对某一个或某部分问题的通用解决方法.而模式语言是对某一领域中多个相关的问题集的解决
方案的集成.这不仅仅是程序设计和软件开发过程的问题,任何领域中,能够描述出原则,主次,方法,
技巧的抽象集合都是模式语言.

如果你还要继续向下看,或者你还有兴趣看我的放水文章.那么请记住这句话:

[学会模式和自如地应用模式进行设计是完全不同的两回事],keep it in your mind!

<放水继续>
记得在清华举行的中国软件业协会JAVA分成立大会上,我向Java程序员推荐过两本哲学的书.
<<全息论>>,<<系统与层次>>.现在我再多加一本中科院哲学所刘长林先生的<<内经的哲学和中医学的方法>>

宏观与微观:
很多时候,我在与一些朋友讨论一些"细节"的内容时,总有些人问"讨论这种问题对解决软件
企业的实际问题有何意义?",不客气地说,这些无知的话题决定了提问者本身就不可能有真正的解决
"企业级应用问题"的能力.

作为一个建筑设计师,他自己当然不会去干抹水泥,码砖头这样的活.但他要比这些码砖头,抹水泥的
小工更清楚每块砖头的强度,年限,每种水泥的凝固时间,粘度等细节参数.否则你的设计再好也只
能建一个豆腐渣工程.

作为一个软件系统架构师,如果不懂你的coder在项目中使用的细节技术,重要组件,重要API实现
你如何保证你的项目的性能和可靠性?难道真的靠默念口诀就能获得一种神奇的效果?

有人说"一个人一生的时间的有限的,把太多的时候花在这些细节上,我就没有更多的时间掌握好些最实用的如structs,spring,hb等",那么我告诉你,如果你不了解最基础的东西,你学三个月structs,spring,hb,我只要一周会

比你掌握更深入,即使Upload这样的小工具,明知道smartUpload的Bug巨多,不好用,有几个人能自动手写出来?当我第一次使用时我就花了40分钟自己写了一个公开在bea论坛上经几年大家的试用还未发现bug,这就是"微观"的重要性.

"设计时脑子里只的模型,对象,实现时调试到汇编!".

这是我一贯以求并一直坚持的架构原则.对于一个架构师而言,与程序设计相关的知识没有主要和
次要之分,从单片机到汇编到到软件工程思想.都是你应该掌握的.听过撒贝宁讲背书的事,因为他老是不上课,
所以复习时不知道哪里是老师划过的重点.好,那我把整本书作为重点背下来!

如何应用模式和模式语言?在你掌握了模式与模式语言的情况下,有三种应用水平:
一是在学会了很多模式后在设计时总是想用上一些模式为了显示我懂设计模式,如果这样你还不
如一点不懂设计模式.Just in time(需要的时候才去做)!
二是在遇到类似问题时能熟悉应用现有模式,这是绝对大多数如我这类中人之资水平能做的事.总之
"不会过日看邻居"一般是不会错的.
三是一种因为掌握了很多模式的基础上,经过不断实践在需要解决问题时没有现成的模式可用时自己
抽象出设计模式.相信自己,你可以做到!

所谓大师能做到举手投足之间都有惊世骇俗之举.嘻笑怒骂之间也能成黄钟大吕之作.而我们常人,
在平凡的生活中总会能悟出一点特别的东西,只不过不如大师那样来得容易.

我把要放水的内容在首节放完,下面就一心一意讲我的设计模式知识.本系列绝不是模式词典.
<放水结束>

这篇文章算是开场,就不连着介绍第一个模式了.但和它相关的是比任何模式更重要的一篇文章.转
贴过来,这才是模式的境界:(注意不是看懂它.而是能做到才是真正的设计模式境界)

A C# Bedtime Story
The following is an excerpt from Windows Forms 2.0 Programming, Chris Sells & Michael Weinhardt, Addison-Wesley, 2006. It's been updated from the original version for C# 2.0.

Once upon a time, in a strange land south of here, there was a worker named Peter. He was a diligent worker who would readily accept requests from his boss. However, his boss was a mean, untrusting man who insisted on steady progress reports. Since Peter did not want his boss standing in his office looking over his shoulder, Peter promised to notify his boss whenever his work progressed. Peter implemented this promise by periodically calling his boss back via a typed reference like so:

class Worker {
Boss boss;

public void Advise(Boss boss) {
this.boss = boss;
}

public void DoWork() {
Console.WriteLine("Worker: work started");
if( this.boss != null ) this.boss.WorkStarted();

Console.WriteLine("Worker: work progressing");
if( this.boss != null ) this.boss.WorkProgressing();

Console.WriteLine("Worker: work completed");
if( this.boss != null ) {
int grade = this.boss.WorkCompleted();
Console.WriteLine("Worker grade= {0}", grade);
}
}
}

class Boss {
public void WorkStarted() {
// Boss doesn't care
}
public void WorkProgressing() {
// Boss doesn't care
}
public int WorkCompleted() {
Console.WriteLine("It's about time!");
return 2; // out of 10
}
}

class Universe {
static void Main() {
Worker peter = new Worker();
Boss boss = new Boss();
peter.Advise(boss);
peter.DoWork();

Console.WriteLine("Main: worker completed work");
Console.ReadLine();
}
}

Interfaces
Now Peter was a special person. Not only was he able to put up with his mean-spirited boss, but he also had a deep connection with the universe around him. So much so that he felt that the universe was interested in his progress. Unfortunately, there was no way for Peter to advise the Universe of his progress unless he added a special Advise method and special callbacks just for the Universe, in addition to keeping his boss informed. What Peter really wanted to do was to separate the list of potential notifications from the implementation of those notification methods. And so he decided to split the methods into an interface:

interface IWorkerEvents {
void WorkStarted();
void WorkProgressing();
int WorkCompleted();
}

class Worker {
IWorkerEvents events;

public void Advise(IWorkerEvents events) {
this.events = events;
}

public void DoWork() {
Console.WriteLine("Worker: work started");
if( this.events != null ) this.events.WorkStarted();

Console.WriteLine("Worker: work progressing");
if( this.events != null ) this.events.WorkProgressing();

Console.WriteLine("Worker: work completed");
if( this.events!= null ) {
int grade = this.events.WorkCompleted();
Console.WriteLine("Worker grade= {0}", grade);
}
}
}

class Boss : IWorkerEvents {
public void WorkStarted() {
// Boss doesn't care
}
public void WorkProgressing() {
// Boss doesn't care
}
public int WorkCompleted() {
Console.WriteLine("It's about time!");
return 3; // out of 10
}
}

Delegates
Unfortunately, Peter was so busy talking his boss into implementing this interface that he didn't get around to notifying the Universe, but he knew he would soon. At least he'd abstracted the reference of his boss far away from him so that others who implemented the IWorkerEvents interface could be notified of his work progress.

Still, his boss complained bitterly. "Peter!" his boss fumed. "Why are you bothering to notify me when you start your work or when your work is progressing?!? I don't care about those events. Not only do you force me to implement those methods, but you're wasting valuable work time waiting for me to return from the event, which is further expanded when I am far away! Can't you figure out a way to stop bothering me?"

And so, Peter decided that while interfaces were useful for many things, when it came to events, their granularity was not fine enough. He wished to be able to notify interested parties only of the events that matched their hearts' desires. So, he decided to break the methods out of the interface into separate delegate functions, each of which acted like a little tiny interface of one method each:

delegate void WorkStarted();
delegate void WorkProgressing();
delegate int WorkCompleted();

class Worker {
public WorkStarted Started;
public WorkProgressing Progressing;
public WorkCompleted Completed;

public void DoWork() {
Console.WriteLine("Worker: work started");
if( this.Started != null ) this.Started();

Console.WriteLine("Worker: work progressing");
if( this.Progressing != null ) this.Progressing();

Console.WriteLine("Worker: work completed");
if( this.Completed != null ) {
int grade = this.Completed();
Console.WriteLine("Worker grade= {0}", grade);
}
}
}

class Boss {
public int WorkCompleted() {
Console.WriteLine("It's about time!");
return 4; // out of 10
}
}

class Universe {
static void Main() {
Worker peter = new Worker();
Boss boss = new Boss();

// NOTE: We've replaced the Advise method with the assignment operation
peter.Completed = new WorkCompleted(boss.WorkCompleted);
peter.DoWork();

Console.WriteLine("Main: worker completed work");
Console.ReadLine();
}
}

And, because Peter was under so much pressure, he decided to advantage of the shorthand notation for assigning delegates provided by C# 2.0:

class Universe {
static void Main() {
...
peter.Completed = boss.WorkCompleted;
...
}
}

Static Listeners
Delegates accomplished the goal of not bothering his boss with events that he didn't want, but still Peter had not managed to get the universe on his list of listeners. Since the universe is an all-encompassing entity, it didn't seem right to hook delegates to instance members (imagine how many resources multiple instances of the universe would need...). Instead, Peter need to hook delegates to static members, which delegates support fully:

class Universe {
static void WorkerStartedWork() {
Console.WriteLine("Universe notices worker starting work");
}

static int WorkerCompletedWork() {
Console.WriteLine("Universe pleased with worker's work");
return 7;
}

static void Main() {
Worker peter = new Worker();
Boss boss = new Boss();

peter.Completed = boss.WorkCompleted;
peter.Started = WorkerStartedWork;
peter.Completed = WorkerCompletedWork; // Oops!
peter.DoWork();

Console.WriteLine("Main: worker completed work");
Console.ReadLine();
}
}

Events
Unfortunately, the Universe being very busy and unaccustomed to paying attention to individuals, has managed to replace Peter's boss's delegate with its own. This is an unintended side effect of making the delegate fields public in Peter's Worker class. Likewise, if Peter's boss gets impatient, he can decide to fire Peter's delegates himself (which is just the kind of rude thing that Peter's boss was apt to do):

// Peter's boss taking matters into his own hands
if( peter.Completed != null ) peter.Completed();

Peter wants to make sure that neither of these can happens. He realizes he needs to add registration and unregistration functions for each delegate so that listeners can add or remove themselves, but can't clear the entire list or fire Peter's events. Instead of implementing these functions himself, Peter uses the event keyword to make the C# compiler build these methods for him:

class Worker {
public event WorkStarted Started;
public event WorkProgressing Progressing;
public event WorkCompleted Completed;
...
}

Peter knows that the event keyword erects a property around a delegate, only allowing clients to add or remove themselves (using the += and -= operators in C#), forcing his boss and the universe to play nicely:

class Universe {
...
static void Main() {
Worker peter = new Worker();
Boss boss = new Boss();

peter.Completed = boss.WorkCompleted; // ERR!
peter.Completed += boss.WorkCompleted; // OK
peter.Started += Universe.WorkerStartedWork; // OK
peter.Completed += Universe.WorkerCompletedWork; // OK

peter.DoWork();

Console.WriteLine("Main: worker completed work");
Console.ReadLine();
}
}

Harvesting All Results
At this point, Peter breathes a sigh of relief. He has managed to satisfy the requirements of all his listeners without having to be closely coupled with the specific implementations. However, he notices that while both his boss and the universe provide grades of his work that he's only receiving one of the grades. In the face of multiple listeners, he'd really like to harvest all of their results. So, he reaches into his delegate and pulls out the list of listeners so that he can call each of them manually:

class Worker {
...
public void DoWork() {
...
Console.WriteLine("Worker: work completed");

if( this.Completed != null ) {
foreach( WorkCompleted wc in this.Completed.GetInvocationList() ) {
int grade = wc();
Console.WriteLine("Worker grade= {0}", grade);
}
}
}
}

public void DoWork() {
...
Console.WriteLine("Worker: work completed");
if( completed != null ) {
foreach( WorkCompleted wc in completed.GetInvocationList() ) {
int grade = wc();
Console.WriteLine("Worker grade= " + grade);
}
}
}

Asynchronous Notification: Fire & Forget
In the meantime, his boss and the universe have been distracted with other things, which meant that the time it takes them to grade Peter's work is greatly expanded:

class Boss {
public int WorkCompleted() {
System.Threading.Thread.Sleep(5000);
Console.WriteLine("Better...");
return 4; // out of 10
}
}

class Universe {
...
static int WorkerCompletedWork() {
System.Threading.Thread.Sleep(1000000);
Console.WriteLine("Universe pleased with worker's work");
return 7;
}
...
}

Unfortunately, since Peter is notifying each listener one at a time, waiting for each to grade him, these notifications now take up quite a bit of his time when he should be working. So, he decides to forget the grade and just fire the event asynchronously:

class Worker {
...
public void DoWork() {
...
Console.WriteLine("Worker: work completed");
if( this.Completed != null ) {
foreach( WorkCompleted wc in this.Completed.GetInvocationList() ) {
wc.BeginInvoke(null, null); // EndInvoke call required by .NET
}
}
}
}

Asynchronous Notification: Polling
The call to BeginInvoke allows Peter to notify the listeners while letting Peter get back to work immediately, letting the process thread pool invoke the delegate. Over time, however, Peter finds that he misses the feedback on his work. He knows that he does a good job and appreciates the praise of the universe as a whole (if not his boss specifically). Plus, hes afraid hes leaking .NET resources acquired by calling BeginInvoke without calling the corresponding EndInvoke method, so, he fires the event asynchronously, but polls periodically, looking for the grade to be available:

class Worker {
...
public void DoWork() {
...
Console.WriteLine("Worker: work completed");
if( this.Completed != null ) {
foreach( WorkCompleted wc in this.Completed.GetInvocationList() ) {
IAsyncResult result = wc.BeginInvoke(null, null);
while( !result.IsCompleted ) System.Threading.Thread.Sleep(1);
int grade = wc.EndInvoke(result);
Console.WriteLine("Worker grade= {0}", grade);
}
}
}
}

Asynchronous Notification: Delegates
Unfortunately, Peter is back to what he wanted his boss to avoid with him in the beginning, i.e. looking over the shoulder of the entity doing the work. So, he decides to employ his own delegate as a means of notification when the asynchronous work has completed, allowing him to get back to work immediately, but still be notified when his work has been graded:

class Worker {
...
public void DoWork() {
...
Console.WriteLine("Worker: work completed");
if( this.Completed != null ) {
foreach( WorkCompleted wc in this.Completed.GetInvocationList() ) {
wc.BeginInvoke(this.WorkGraded, wc);
}
}
}

void WorkGraded(IAsyncResult result) {
WorkCompleted wc = (WorkCompleted)result.AsyncState;
int grade = wc.EndInvoke(result);
Console.WriteLine("Worker grade= {0}" + grade);
}
}

Anonymous Delegates
At this point, Peter is using delegates to notify interested parties in the process of his work and using delegates to get notified when grades are available on the work hes completed. The delegates provided by his boss and the universe are provided by separate entities, so it makes sense that they are encapsulated in methods on those entities. However, in the case of the WorkGraded method, theres really no good reason for this to be a separate method except the syntactic requirements of C# 1.0. As of C# 2.0, Peter can drop the code required to handle the processing of his work grade into an anonymous delegate:

class Worker {
...
public void DoWork() {
...
Console.WriteLine("Worker: work completed");
if( this.Completed != null ) {
foreach( WorkCompleted wc in this.Completed.GetInvocationList() ) {
wc.BeginInvoke(delegate(IAsyncResult result) {
WorkCompleted wc2 = (WorkCompleted)result.AsyncState;
int grade = wc2.EndInvoke(result);
Console.WriteLine("Worker grade= {0}", grade);
},
wc);
}
}
}
}

Here, instead of passing in the name of a method to call when his work has been graded, hes passing in the body of the method itself as designated with a different use of the delegate keyword to create a method with no name (and therefore anonymous). The body of the method is fundamentally the same in that Peter still passes the WorkCompleted delegate as a parameter to BeginInvoke and then pulls it out of AsyncState for use in extracting the result. However, one of the benefits of anonymous delegates that Peter knows is that he can make use of the variables in the surrounding context from within the anonymous delegate body, causing him to rewrite his code thusly:

class Worker {
...
public void DoWork() {
...
Console.WriteLine("Worker: work completed");
if( this.Completed != null ) {
foreach( WorkCompleted wc in this.Completed.GetInvocationList() ) {
wc.BeginInvoke(delegate(IAsyncResult result) {
// Use wc variable from surrounding context (ERR!)
int grade = wc.EndInvoke(result);
Console.WriteLine("Worker grade= {0}", grade);
},
null);
}
}
}
}

This code compiles just fine, but when its run, it will cause the following exception to be thrown:

System.InvalidOperationException:
The IAsyncResult object provided does not match this delegate.
The problem is that while the wc variable is allowed to be used in the anonymous delegate, its still being used by the for-each statement. As soon as the asynchronous invocation begins, the wc variable changes and the delegate used to start things (wc) no longer matches the async result passed as an argument to the anonymous delegate. Peter slaps his head and creates a hybrid solution:

class Worker {
...
public void DoWork() {
...
Console.WriteLine("Worker: work completed");
if( this.Completed != null ) {
foreach( WorkCompleted wc in this.Completed.GetInvocationList() ) {
// Create an unchanging variable referencing the current delegate
WorkCompleted wc2 = wc;
wc.BeginInvoke(delegate(IAsyncResult result) {
// Use wc2 variable from surrounding context
int grade = wc2.EndInvoke(result);
Console.WriteLine("Worker grade= {0}", grade);
},
null);
}
}
}
}

public void DoWork() {
...
Console.WriteLine("Worker: work completed");
if( completed != null ) {
foreach( WorkCompleted wc in completed.GetInvocationList() ) {
wc.BeginInvoke(new AsyncCallback(WorkGraded), wc);
}
}
}

void WorkGraded(IAsyncResult res) {
WorkCompleted wc = (WorkCompleted)res.AsyncState;
int grade = wc.EndInvoke(res);
Console.WriteLine("Worker grade= " + grade);
}

Happiness in the Universe
Peter, his boss and the universe are finally satisfied. Peter's boss and the universe are allowed to be notified of the events that interest them, reducing the burden of implementation and the cost of unnecessary round-trips. Peter can notify them each, ignoring how long it takes them to return from their target methods, while still getting his results asynchronously and handling them using anonymous delegates, resulting in the following complete solution:

delegate void WorkStarted();
delegate void WorkProgressing();
delegate int WorkCompleted();

class Worker {
public event WorkStarted Started;
public event WorkProgressing Progressing;
public event WorkCompleted Completed;

public void DoWork() {
Console.WriteLine("Worker: work started");
if( this.Started != null )
this.Started();

Console.WriteLine("Worker: work progressing");
if( this.Progressing != null )
this.Progressing();

Console.WriteLine("Worker: work completed");
if( this.Completed != null ) {
foreach( WorkCompleted wc in this.Completed.GetInvocationList() ) {
WorkCompleted wc2 = wc;
wc.BeginInvoke(delegate(IAsyncResult result) {
int grade = wc2.EndInvoke(result);
Console.WriteLine("Worker grade= {0}", grade);
},
null);
}
}
}
}

class Boss {
public int WorkCompleted() {
System.Threading.Thread.Sleep(3000);
Console.WriteLine("Better...");
return 5; // out of 10
}
}

class Universe {
static void WorkerStartedWork() {
Console.WriteLine("Universe notices worker starting work");
}

static int WorkerCompletedWork() {
System.Threading.Thread.Sleep(4000);
Console.WriteLine("Universe pleased with worker's work");
return 7;
}

static void Main() {
Worker peter = new Worker();
Boss boss = new Boss();
peter.Completed += boss.WorkCompleted;
peter.Started += Universe.WorkerStartedWork;
peter.Completed += Universe.WorkerCompletedWork;
peter.DoWork();

Console.WriteLine("Main: worker completed work");
}
}

Peter knows that getting results asynchronously comes with issues, because as soon as he fires events asynchronously, the target methods are likely to be executed on another thread, as is Peter's notification of when the target method has completed. However, Peter is good friends with Mike, who is very familiar with threading issues and can provide guidance in that area.

And they all lived happily every after.

The end.

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值