显示标签为“thread”的博文。显示所有博文
显示标签为“thread”的博文。显示所有博文

2009年4月15日星期三

Tcl Blend中的线程和event处理

Jacle和Tcl Blend中的tcl.lang.Notifier实现了事件循环处理功能。 一个线程可以包含N个Tcl解释器,在同一个线程中创建的解释器Interp共享同一个Notifer。也就是Notifier是和绑定创建Interp时的线程对应在一起的。最简单的情况,一个线程包含一个Tcl解释器和一个Notifier对象。
看看Interp的构造函数的定义就清楚了:

cThread = Thread.currentThread();
notifier = Notifier.getNotifierForThread(cThread);
notifier.preserve();

Notifier.getNotifierForThread()定义是:

public static synchronized Notifier getNotifierForThread( Thread thread)
{
Notifier notifier = (Notifier) notifierTable.get(thread);
if (notifier == null) {
notifier = new Notifier(thread);
notifierTable.put(thread, notifier);
}

return notifier;
}



通常,事件是在一个循环中通过不断的调用doOneEvent()方法得到处理。
而TclBlend的Event包含了你要完成的逻辑。如果事件队列中没有event,则doOneEvent()会被阻塞,直到获得新的事件。所以,一般创建一个独立的线程来运行Notifier.doOneEvent()。而tcl event都是在其它的线程中创建并通过interp.getNotifier().queueEvent(event, TCL.QUEUE_TAIL) 放到event query中。

调用event.sync(); 会阻塞,直到这个event被处理完毕。

从不是运行doOneEvent()的线程中调用interp.eval()是不合法的,通常导致crash和随机的错误。

If you want to use the tclblend in your java project, you need not only the tclBlend libraries but also the jacl libraries. You should copy the tclBlend and jacl jar files into the same directory otherwise tclBlend it can not work rightly.

2009年2月16日星期一

Executor为谁而生?---- 执行策略

Executor与Runnable的定义极为相似。为什么要加上个Executor?
public interface Executor {
    void execute(Runnable command);
}

public interface Runnable {
    void run()
}

Runnable规范了任务的入口。Thread提供了驱动任务的线程接口。
有了这两个interface,你就可以完成最简单的多线程程序。但是如果你实现一些执行策略,你将面临非常有挑战性的编程。什么是执行策略?执行策略定义了用线程执行任务的一些方式。如,可以有多少个任务可以并行?何种顺序(FIFO, FIFO,自定义的优先级算法)?最多有多少任务可以等待?如果超过了可等待的任务数,系统如何反应? 任务执行前做什么?任务执行后做什么?诸如此类。
Runnable和Thread没有从API上提供解决"执行策略"的任何线索。要实现你特定的执行策略就需要你利用Thread的sleep(),wait(),notify(), interrunppt(),isInterrupt()进行高水平的设计。
如果你的系统不同模块需要使用不同的执行策略,如果你的系统需要动态的调整执行策略,这些功能是不是更具挑战性?

Executor就是Thread和Runnable之间的一层隔离、解耦。这层隔离为Thread和Runnable之间实现多种交互提供了可能与方便。Executor的各种子类实际上就实现了各种不同的执行策略。

Exector的是java.util.concurrent的基石。

Link:Executor API
All Known Subinterfaces:
ExecutorService, ScheduledExecutorService
All Known Implementing Classes:
AbstractExecutorService, ScheduledThreadPoolExecutor, ThreadPoolExecutor

2009年2月12日星期四

控制线程 ---- 停止

写多线程的程序需要对并发带来的危险(竞争、死锁、脏读...)进行预防。同时还有一个重要的方面是对那些并行执行的代码进行控制,比如:停止、等待、继续。

让线程驱动的代码要么是被while括起来的需要长时间运行;要么是个临时的任务,运行到最后让线程自动终结。

为什么停止线程是个问题?因为线程的阻塞状态。
线程的四个状态:New, Runnable, Blocked, Dead
导致thread blocked的四个原因: sleep(sometime)、 wait()、I/O 操作、 synchronized导致的锁竞争。

需要停止线程操作的通常都是要终止长时间运行的线程。让线程停下来,大致有以下几种方法:
1)利用代码中while的条件判断语句,检测一(多)个是否需要停止的标志。
这个标志的访问需要同步控制。同时可以被其它线程代码改变。
2)使用interrupt()方法,让线程正在驱动的的代码产生InterruptedException
你可以在代码中捕获这个exception,进行适当的判断,然后让线程优雅的停止。
3)但是第二种方法对于因为以下两种原因造成的阻塞不起作用:IO和等待进入synchronized块。
针对IO造成的阻塞,可以关闭IO资源,这样迫使IO操作产生exception而让线程从阻塞状态返回,从而获得停止线程的机会。
4)因为等待锁而阻塞的线程利用传统的thread的api似乎没有什么好办法。一个建议是不要长时间的占用那些很多代码都想获得的锁
5)在提倡使用concurrent包的情况下。可以利用Executor.shutdownNow()停止所有其管理的线程。利用Executor.submit()获得的Future cancel(true)来对单独的线程进行停止操作。

**
现象:
启动一个线程执行一串代码,其中某步抛出了Error,注意是error不是Exception。这意味着你的线程嘎然而止。这种退出方式实在是太暴力了。:)
实践:
1)需要在你的线程执行的代码中捕获任何异常、错误。
Throwable 是Error、Exception的父类。而Exception又分为Runtime的exception和不是runtime的。我的实践是在run()的代码中catch (Throwable t),这样就可以保证你不遗漏任何异常了。
2)对产生错误的原因进行分析。具体问题具体分析。可以或应该继续执行的,请记录异常信息,继续代码的执行。如果需要退出线程的,需要注意释放在线程执行中申请的资源以及修改程序状态标志(某些应用数据)。

2009年2月3日星期二

对synchronized 的又一点理解

java里的synchronized给我的一个强烈印象是“同步资源”,或者说是利用对象的monitor进行序列化访问的手段。
当使用object.wait()而没有事先synchronized object时,问题出现了:IllegalMonitorStateException。

wait() 之前必须先过的这个对象上的锁。这就要依靠synchronized。因为
synchronized的基本作用是“获取对象上的monitor”。所谓的同步、序列化访问都是建立在monitor的作用之上的。也许把synchronized理解成获得锁的语句可能更接近事实。


sleep() yield()并没有释放锁,这是和wait()的巨大区别。