In .Net it feels pretty natural to me. It just adapts the mechanism used in "lower lever systems", passing a function pointer to Win32 CreateThread or Posix pthread_create. In .Net the perfect match for function pointers are delegates, so for whatever method you want to launch in its own thread (well, provided that it's parameterless or expects just one Object parameter), you just create a delegate about that method, then create a Thread object taking that delegate as parameter and that's all:
Thread th = new Thread(new ThreadStart(p1.SayHi));
th.Start();
simple, right?
Unfortunately in Java it's a different story. There is not equivalent to function pointers, so how do you feed the Thread object with the code it's going to launch? Two options here, either you inherit from Thread or implement the Runnable interface, putting in both cases your code in the run method. I clearly prefer the second option, as inheriting from Thread is rather limiting. If you program a method with the idea of being run in a separate thread implementing Runnable does not sound bad, but if what you want to run in a separate thread is a normal method programmed by others without threading in mind (which I think is perfectly valid, I don't see why "I'm going to run in a separate thread" should be part of the design of a method, I see it as running asynchronous, it can be the client who decides how to run such method), things get rather ugly. You would need to create an extra class implementing Runnable and invoke that method from inside the run method:
class RunnableSayHiWrapper implements Runnable
{
Person p1;
public RunnableSayHiWrapper(Person p)
{
this.p1 = p;
}
public void run()
{
this.p1.sayHi();
}
}
Person p2 = new Person("xuan");
Runnable runnable2 = new RunnableSayHiWrapper(p2);
Thread th2 = new Thread(runnable2);
th2.start();
In many cases we can avoid typing the extra class by using an anonymous inner class, but given that as of today Java does not have closures yet... we have to resort to some unclean artifacts:
static Person p1 = new Person("xana");
public static void main(String[] args)
{
Runnable runnable = new Runnable() {
public void run() {
p1.sayHi();
}
};
Thread th1 = new Thread(runnable);
th1.start();
What I'm referring to as unclean is that to have a reference to p1, we've had to add it to the class containing the calling method (in this case it's a static). The elegant thing would be that we just could pass that instance to the calling method and we had a closure that would trap that parameter as part of the lexical scope, but the lack of closures is a real pain...
Hopefully, if you're a JVM programmer you still can have fun and enjoy programming by using a beautiful, modern, powerful and fast evolving language like Groovy, and put aside all these Java limitations. Groovy has closures, and futher on, you can as if by magic implement interfaces with closures or maps, so creating a Runnable that you can feed to your Thread object is that simple and beautiful like this:
def p1 = new Person("xana");
def runnable1 = { p1.sayHi(); } as Runnable
th1 = new Thread(runnable1);
th1.start();
Furthermore, as we're using a closure, we get rid of one of the annoying limitations of .Net ThreadStart delegate, the number of parameters (0 for ThreadStart delegate or 1 for ParameterizedThreadStart delegate) of the method. There's no problem if our method (formatName in the example below) expects x parameters, as long as we have those variables available in the code where the closure is defined, they will get trapped:
s1 = "{";
s2 = "}";
def p3 = new Person("xabel");
def runnable3 = { p3.formatName(s1, s2); } as Runnable
th3 = new Thread(runnable3);
th3.start();
Of course, doing the same in C#, thanks to closures, is equally simple and elegant. By the way, the job done by the C# and Groovy compilers when dealing with anonymous methods-closures and Groovy closures is rather similar. In both cases a new class with fields for the trapped variables and a method for the code inside the closure are created under the covers.
string st1 = "{";
string st2 = "}";
Person p1 = new Person(){Name = "Xuan"};
//we need a closure here for the st1, st2 parameters
Thread th1 = new Thread( () => p1.FormatName(st1, st2));
th1.Start();
Sometimes code can be astonishing beautiful, don't you think?
You can download source code here.
No comments:
Post a Comment