Java Threads and SugarCubes

Java Threads and SugarCubes1

Frédéric BOUSSINOT, Jean-Ferdy SUSINI
EMP-CMA INRIA/Meije
2004 route des Lucioles
F-06902 Sophia-Antipolis

Frederic.Boussinot@sophia.inria.fr
Jean-Ferdinand.Susini@sophia.inria.fr

October 1998

Abstract

SugarCubes is a set of Java classes which provides a simple and structured approach to concurrency. It offers a powerful and modular communication mechanism based on instantaneously broadcast events. The semantics of SugarCubes code is deterministic and does not depend on arbitrary choices made by the execution platform. SugarCubes gives programmers a sound and fine control over execution; for example, reactive instructions can be preempted in a totally clean way. The paper compares the use of threads and the one of SugarCubes. It shows that SugarCubes is simpler and more structured than the standard Java threading mechanism.

Keywords: Concurrency, Java, Threads, SugarCubes

1 Introduction

The reactive approach promotes the use of SugarCubes[BS] which is a set of Java[GJS] classes for concurrent reactive programming. The goal of this text is to compare SugarCubes with the standard threading features of Java, and to justify its use.

SugarCubes defines reactive instructions and reactive machines for concurrent reactive programming in Java. A reactive instruction is a kind of thread (one should call it a "logical thread") describing a behavior and its associated state. The difference with threads is that a reactive instruction is intended to be executed by a reactive machine, not by a thread scheduler. More precisely:

Advantages over threads are the followings: Disadvantages, of course, also exist: Anyway, threads and SugarCubes are fully compatible: To compare threads and reactive instructions, we choose the small example of two concurrent activities sharing the same object and needing some synchronization. More precisely, one activity consists in animating a graphical icon following a sinusoidal path (sine), and the other consists in animating the same icon following a sinusoidal path with a shift of p/2 (cosine). The icon is of course the shared object. Naively, the intended behavior of the icon is to draw a circle, as natural combination of sine and cosine moves. Synchronization of the two activities is the mean to get this behavior.

Of course, the example could appear as a rather academic exercise as one can simply get the circular behavior without using any concurrency at all. But a sequential solution would clearly be less modular and less flexible than a concurrent one. Anyway, the goal of this text is to compare two solutions to deal with concurrency, not to compare concurrency and sequentiality.

Finally, although the considered example is very simple, we feel that it is sufficient to enlighten the main characteristics and problems of the two approaches.

The text has the following structure: SugarCubes is briefly described in section 2. The example and its coding using threads are given in section 3. Section 4 contains the SugarCubes coding of the example. Finally, in section 5, one briefly considers the SugarCubes possibilities of fine control over reactive instructions and the use of broadcast events.

The Java threading mechanism, described for example in [GJS] or in [OW], is assumed to be known by the reader.

The codes described in this text are available on the Web2.

2 SugarCubes Overview

The aim of the reactive approach is to propose a flexible programming of reactive systems, especially those which are dynamic (that is, the number of components and their connections are changing during execution). Information on this approach can be found on the Web3. SugarCubes implements the reactive approach in Java.

The two main notions of SugarCubes are reactive instruction whose semantics refer to instants, and reactive machine whose purpose is to execute reactive instructions in an environment made of instantaneously broadcast events.

2.1 The Class Instruction
The class Instruction implements reactive instructions which can be activated (method activ), reset (method reset) , or forced to terminate (method terminate). Each activation returns as result one of the three following values:

A call to method terminate forces the instruction to completely terminate and thus to return TERM when activated. A call to method reset resets the instruction which thus returns in its initial state.

The basic reactive instruction of SugarCubes are:

2.2 The Class Machine
The class Machine implements reactive machines. A reactive machine executes a program which is a reactive instruction. It performs two main tasks: first, it decides the ends of instants, and second, it deals with broadcast events. Initially, the program is the Nothing instruction which does nothing and terminates instantaneously. New instructions are dynamically added to the program (by calling the method add) and executed in parallel with the previous ones, using the Merge parallel instruction.

Basically, a reactive machine detects the end of the current instant, that is when all parallel instructions of the program are terminated or stopped. The behavior is as follows:

Reactive machines are reactive instructions (class Machine extends Instruction). To execute one instant of the program of a machine, one call the method activ of the machine, with the machine as parameter of the call.

3 Coding with Threads

Now, let us code in Java the example described in the introduction. Consider the Java class for moving a small icon following a sinusoidal path:

Two threads are defined, which cyclically call the sine and cosine methods. Code of SinMove is (CosMove is similar, except that cosine replaces sine):

Finally an applet is defined which concurrently runs the two previous threads:

How does the applet behave on screen? Naively, one expects that the icon would draw a circle, as a natural combination of sine and cosine moves. Unfortunately, this is not the case: with a cooperative platform, the first executed thread of SinMove or CosMove would never give up the control and the icon would not move at all on screen (the graphical thread would be starved); with a preemptive platform, one would get arbitrary moves, depending on the way threads are scheduled.

3.1 Use of Yield
The natural solution to let the applet run on cooperative platform is to call the yield() method in SinMove and in CosMove, to let them give up control. The method run of SinMove becomes (CosMove is changed in the same way):

Now, the applet does not starve with cooperative platforms. Moreover, one gets a circle with cooperative platform, as sine and cosine are executed in turn. Thus, one obtains a very simple solution for cooperative platforms. Unfortunately, it does not work with preemptive ones, as yield() calls do not prevent the platform from scheduling tasks in an arbitrary way.

3.2 Synchronization
The two threads SinMove and CosMove must be synchronized to execute in turn, independently of the preemption actions performed by the platform.

The standard solution in Java consists in using the two wait and notify primitives. It is as follows: first, SinMove takes a lock on the shared icobj object; then, it executes the sine method and notifies the other thread; finally, it waits for a notification coming from the other thread which behaves in the same way. Code for SinMove is:

Now, one gets a solution which runs the two threads in turn, on both cooperative and preemptive platforms.

Please note that, as a side effect of synchronization, the shared object icobj becomes protected: sine and cosine are actually atomic actions. This can be an important matter when several threads can change the same icobj coordinate. Let us suppose for example that a second instance of SinMove is added. Then, if accesses where not atomic, the two instances calling sine could interleave their statements with an unforeseeable result.

This solution is however not satisfactory as the icon jumps unsmoothly from a point to another on the circle trajectory. The reason is that the graphical thread does not have enough time for processing the drawing orders. Actually, there is a lack of synchronization with the graphical thread. The problem is that Java does not give any way to synchronize with it !

3.3 Use of Sleep
One standard solution is to slow-down the user threads, using sleep, to let enough time to the graphical thread to do its job. Method run of SinMove becomes:

Now, the icon smoothly draws a circle. However, one must adjust the sleeping time to the machine execution speed (in the present case, 15 milliseconds, for a Sun ultra sparc 1 station). The solution is, thus, not portable.

3.4 Synchronous Updating
Another way to compensate the lack of synchronization with the graphical thread, is to force an applet update after each move, using the applet method update (which returns upon graphics update completion). The program must be changed, to give SinMove and CosMove access to the applet. SinMove becomes (CosMove is changed in the same way):

There is no more problem with the graphical thread as it has nothing to do now, except processing graphical events, of no use in this case.

3.5 General Solution
In the previous solution, the graphical update actions are performed after each move. Unnecessary updates exist because, as SineMove and CosMove are executed in turn, only one update over two is really needed. A possibility would be to break the symmetry between SinMove and CosMove and to let only one of them perform the update actions. But the problem of redundant updates would not be definitively solved; it would, for example, rise up again when animating two icons, instead of a single one. To solve it in all cases, one can design a more general, but more complicated, solution based on the following protocol:


Now, the system can be extended without problem to several icobjs and the number of graphical updates is minimal.

As the corresponding code is more complicated than the one of the ad hoc solution of section 3.4, it is not given here.

4 Coding with SugarCubes

4.1 SugarCubes Code
SugarCubes is now used to code the example. As applets are used, one first extends the class Machine and defines the new class ReactiveApplet which implements Runnable:

The machine is cyclically activated (being its own execution context) while its program is not terminated and it updates the graphics at each instant.

SinMove is defined as an atom which action consists to call sine (atom CosMove which calls cosine is defined exactly in the same way):

The reactive instruction which cyclically executes atom SinMove is defined by:

new Loop(new Seq(new SinMove(icobj),new Stop()))

It is the counterpart of:

for(;;){ icobj.sine(); Thread.yield(); }

The applet SinCos now performs the following actions: a reactive applet mach is first defined; then, two reactive instructions are added to the reactive applet; finally, the reactive applet is started:

4.2 Comparison with Threads
Comparison with the thread-based code of section 3 is as follows:

The code in SugarCubes is actually very close to the thread-based one, described in section 3.1, that runs on cooperative platforms. However, the thread-based code is incorrect as it does not run properly on preemptive platforms, while the SugarCubes code is correct and behaves exactly in the same way in any context.

The syntax of SugarCubes instructions is not very friendly, as it forces the programmer to use instruction constructors; the aim of Reactive Scripts[BS2] is to give a simple and compact syntax which is easily translated into SugarCubes. For example, the previous example is typed as a reactive script as:

loop { icobj.sine() }; stop end || loop { icobj.cosine() }; stop end

4.3 Benchmarks
We have performed some benchmarks on the previous codes running on several platforms (Sun UltraSparc 1 and 10, Macintosh G3, and Linux on a PC Pentium 2 Pro). We have computed the number of frames per second (FPS) performed by the system during execution (the instrumented code used is available on the Web). Moreover, for the general thread-based solution and for the SugarCubes one, the cases of 2 and 3 icobjs have been considered.

Results are highly dependent of graphics orders being processed or not. Globally, when graphics orders are processed, SugarCubes is faster on Sun platforms (both 1 and 10) and very close to the generic thread-based solution on Linux. When graphics orders are not processed (programs are not executed as applets and method update does nothing), SugarCubes are, on Sun, slower with one icobj, but faster with two or three; on Linux, SugarCubes is always slower than thread-based solutions. Measures on Macintosh differ completely with the two versions of the MRJ used (2.0 and 2.1 alpha).

All these results are rather difficult to interpret and we feel that more work has to be done on that subject, including other examples. Moreover, the new version of the SugarCubes which is in preparation should be faster than the current version 1, and comparison should be continued when available.

5 Control over Execution

5.1 Terminating a Reactive Instruction
SugarCubes gives the programmer a complete control over the execution of a reactive instruction. For example, reactive instructions can be forced to terminate using the Until preemption operator. This operator is actually the counterpart4 of Thread.stop which is a source of problems and is deprecated in the new version 1.2 of Java (see [JSD] for details).

Let us suppose that one wants to reverse the icobj move direction from inverse clock-wise to clock-wise. Two new methods are introduced in Icobj to reset the angles, and to move backward:

Two atoms ResetAngle and InvSinMove are also defined to call the previous methods. Now, to reverse the icobj trajectory becomes possible with the following reactive instruction:

On generation of the event change, the loop executing SinMove is abandoned and ResetAngle is executed; then, the Until instruction is terminated and control immediately goes to the second loop executing InvSinMove.

Please note that reactive statement preemption is structured: the Until instruction can only preempt its body (the first loop). This is certainly a good point compared to the lack of structure of Thread.stop() with which one can preempt any thread as soon as a reference on it is available.

5.2 Broadcast Events
SugarCubes provides the programmer with a powerful communication based on broadcast events. To show the power of broadcast, let us consider a system made of two icobjs and suppose than one wants to simultaneously reverse their trajectory. The solution with broadcast events is extremely simple and natural: basically, there is nothing to do! the two reactive instructions that pilot icobjs share the same event change which is broadcast; thus, the two trajectories change at the very same instant, when change is generated.

Using the local declaration instruction EventDecl, one can restrict the scope of event change, and, for example, invert the trajectory of only one of the two icobjs.

Event broadcast is modular. Suppose that new activities can be dynamically introduced into the system. Then, there is no need to change anything when a new instruction becomes active, as it automatically gets the same vision of events than the others instructions have (provided, however, that introductions of new instructions take place at the very beginning of instants). Modularity also allows the programmer to introduce "observers" (instructions that do no generate any event) in a system with the guaranty that it will have no effect at all on the others components.

6 Conclusion

6.1 Difficulties of Thread Programming
Threads programming is a difficult exercise, in which one must deal with several overlapping notions, such as priorities, scheduling policies, or accesses to shared resources. Basically, one can identify two main problems:

The first problem is that a thread-based system must run on BOTH preemptive and cooperative platforms. Adding yield() calls is necessary for cooperative context, but does not solve the problem with preemptive ones: with preemptive platforms, yielding actions issued from the system and from the program are inextricably mixed; it is like if somebody behind the scene was dropping yielding actions in a totally uncontrolled (and may be unfriendly) way. To change this situation and regain control over preemptive platforms, programmers have to implement complex strategies. Programming becomes over-complicated and programmers must be very conservative and cautious to control all consequences of possible switches of control.

The second problem appears when threads need to communicate; for this purpose, threading systems only offer low-level primitives based on the notion of a lock. Thread programmers often try to overcome the problem by limiting communications and synchronizations as far as possible. Actually, they try to limit at the maximum the use of concurrency which leads to monolithic, rigid, and non-modular programming.

6.2 Benefits for Using SugarCubes
The semantics of SugarCubes code does not depend on arbitrary choices made by the reactive machine executing it. Semantics is completely captured by the code. This is a deep difference with threads, the semantics of which depends on the underlying execution platform.

Coding with SugarCubes is actually very close to coding assuming a cooperative platform. Scheduling is basically performed "by hand" (using Stop, the counterpart of yield) and the programmer has to control how concurrent activities are fragmented. This leads to deterministic executions with a clear semantics. Cooperative strategies lower the needs of synchronizations to implement atomic accesses to data.

Using the preemption operator of SugarCubes, termination of reactive instructions can be forced in a completely safe way; the preempted instruction is allowed to finish the current instant and is thus in a stable state when preemption takes place.

SugarCubes offers a powerful and modular communication mechanism based on instantaneously broadcast events. This frees the programmer from always re-implementing high-level communication mechanisms each time threads must communicate.

SugarCubes is a simple and structured approach to concurrency which can be seen as an alternative to Java threads, especially when dynamic combinations of communicating concurrent components are needed. At least, it provides a useful technique complementary to the one of threads.

Acknowledgments

Thanks to Laurent Hazard, Jean-Pierre Paris, and Jean-Marc Tanzi for their remarks on a previous version of the paper.

References

[BS] F. Boussinot, J-F. Susini, The SugarCubes Tool Box - A reactive java framework,  Software Practice & Experience, 28(4), 1531-1550, December 1998.
A research report is available at: http://www.inria.fr/meije/rc/SugarCubes/

[BS2] F. Boussinot, J-F. Susini, The SugarCubes Tool Box - Rsi-Java Implementation, research report available at: http://www.inria.fr/meije/rc/SugarCubes/

[GJS] J. Gosling, B. Joy, G. Steele, The Java Language Specification, Addison-Wesley, 1996.

[Ho] Allen Hollub, Programming Java threads in the real world, Part 1, JavaWorld, September 98, available at: http://www.javaworld.com/jw-09-1998/jw-09-threads.html

[JSD] JavaSoft, Why JavaSoft is Deprecating Thread.stop, Thread.suspend and Thread. resume, JavaSoft Documentation, available at:
http://java.sun.com/products/jdk/1.2/docs/guide/misc/threadPrimitiveDeprecation.html.

[OW] Scott Oaks, Henry Wong, Java Threads, O'Reilly, 1997.



1 With support from France Telecom/CNET

2 http://www.inria.fr/meije/rc/SugarCubes/ComparisonThreadsSugarCubes/

3 http://www.inria.fr/meije/rc/

4 Unfortunately, there is a kind of name clash: Thread.stop forces the termination of a thread, although in SugarCubes Stop only ends execution for the current instant. Actually, the counterpart of Thread.stop is obtained with the Until preemption reactive instruction.