Bootstrap

Unmi 学习 Groovy 之多线程

Java 的一个很值得称道的特性是在语言级支持多线程。定义了 Runnable 接口,并在根类 Object 中提供了 wait/notify 方法,还有 synchronized 关键字的支持。我们常说实现多线程的方式有两种:继承 Thread 和实现 Runnable,实质上工作者线程执行的都是 Runnable 接口中定义的 run() 方法,Thread 本身实现了 Runnable 接口,它不过是为线程的调度使用提供了许多有用的方法。

而 Groovy 作为 Java 家族的皇储(安心做储君就行了),多线程方面自然不会甘拜下风。Groovy 通过 MetaClass 对 java.lang.Thread 进行了扩展,即所谓的 GDK - Groovy methods added to Java SE classes。

在原 java.lang.Thread 类中增加了两个方法,分别是:

static Thread start(Closure closure);
static Thread startDaemon(Closure closure);
//对应的是 Daemon 线程

这两个方法接受的参数是闭包,要知道所有的闭包都是继承自 groovy.lang.Closure,而它是实现了 java.lang.Thread 的。所以使用闭包很容易实现 Groovy 中的多线程。具体到代码上就有以下几种写法:

Code    View Copy Print
  1. t = new Thread() {/* Closure body */};   
  2. t.start();   
  3.   
  4. Thread.start { /* Closure body */};   
  5.   
  6. Thread.startDaemon { /* Closure body */};   

这样,闭包中的代码就会在一个新的线程中执行。真的是这样吗?最能加深印象的做法是我们用一段代码来测试一下,可以从两方面来观察,满足一项即可:

1. 闭包外部和内总分别打印出当前的线程名看是否不一样,是则为不同线程

2. 看闭包中的操作是否要阻塞主线程,不会则表示在新线程中执行的闭包

Code    View Copy Print
  1. println "Outter thread: "+Thread.currentThread().getName();   
  2. Thread.start {   
  3.         println "Inner thread: "+Thread.currentThread().getName();   
  4.         "How are you?".each {   
  5.                 print it;   
  6.         }   
  7. }   
  8. println "Fine, thank you.";   

我执行后的输出都是:

Outter thread: main
Fine, thank you.
Inner thread: Thread-1
How are you?

线程名不一样,并且最后一行代码有机会在闭包执行之前或之中执行,从这两者中任一条件说明了闭包是在新线程中执行的。试着以通常的方式调用闭包,就是不一样的情况了。

Java 中可以使用 Timer 和 TimerTask 来实现定时任务--在新线程中执行。Groovy 对此 java.util.Timer 也有相应的扩展,增加了 runafter(int delay,Closure closure) 方法。因此在 Groovy 中应用 Timer 也能实现多线程:

Code    View Copy Print
  1. new Timer().runAfter(1000) { /* Closure body */}   

其他与线程相关 wait/notify 和 synchronized 控制,在使用上与 Java 差不多的。

知其然,然后总希望知其所以然,否则不免觉得有物梗梗于心头似的。至少能大概了解一下 Groovy 对于多线程的实现原理。

Thread.start()Thread.startDaemon() 最终实际调用的是 org.codehaus.groovy.runtime.DefaultGroovyStaticMethods 的相应两个方法:

public static Thread start(Thread self, Closure closure);
public static Thread startDaemon(Thread self, Closure closure)

在 IDE 中,引入 Groovy 的源代码,在这两个方法上打上两个断点就能发现 Thread.start() 大约是循着,以下路径到来的:

ScriptBytecodeAdapter.invokeMethodN()->InvokerHelper.invokeMethod()->MetaClassImpl.invodeStaticMethod()->...->NewStaticMetaMethod.invoke()->...->DelegatingMethodAccessorImpl.invoke()->..->NativeMethodAccessorImpl.invoke0()->DefaultGroovyStaticMethods.start();

Timer.runAfter() 方法最终调用的是 org.codehaus.groovy.runtime.DefaultGroovyMethods

public staic TimerTask runAfter(Timer timer, Closure closure) 方法。

这些新方法是在 Groovy 运行时通过 MetaClass 新引入到原有 Java 类中的,或是对已引入过的方法会从 CacheMethod 中取用。

由此及彼,通过对上面过程的了解,我们发现了有几个 Groovy 类很值得去关注的,那就是:

org.codehaus.groovy.runtime.DefaultGroovyMethods

org.codehaus.groovy.runtime.DefaultGroovyStaticMethods

org.codehaus.groovy.runtime.DefaultGroovyMethodsSupport

留意这三个类(尤其是第一个类)中的方法,你会发现所有的 Groovy 对 Java 类的扩展都能在其中找到。或许能称之为 GDK 扩展。瞧瞧 DefaultGroovyMethod 的个头,一万多行代码,再看看 Groovy 对 DefaultGroovyMethod 的类说明:

This class defines all the new groovy methods which appear on normal JDK classes inside the Groovy environment. Static methods are used with the first parameter the destination class.

这让您觉得像 Groovy 中类似 each()splitEachLine()any() 这样的方法大可不必如先前那般惊讶了。

;