Monday, February 12, 2018

Groovy: Advanced Concepts: Concurrency

Groovy supports multithreaded operation, deriving its capabilities from the Java language itself.  This includes thread-based processing and concurrent processing capabilities.
In order to implement concurrency, Groovy operates on a single-process, multi-threading model.  Each process contains at least one thread and it has the ability to create multiple additional threads, each capable of performing separate work.  These threads share access to the same data, so the language implements a set of critical region and semaphore capabilities to allow for protecting access and updates to shared memory.

Control and Creation

The core language object in Java, and by extension Groovy, for managing multi-threading are java.lang.Thread and java.lang.Runnable.  The Thread object represents a single thread of execution within a Java process.  The language allows a developer to create, monitor and affect the processing of a given thread.
In order to create a thread, a class must be created that implements the Runnable interface.  This interface offers a single run method that is the developer will implement and provide the main entry point for the thread to start execution.  The developer will first create an instance of the class, then create the Thread object using the new class and finally calling the Thread.start() method to being processing.
Threads in Groovy are interruptible and include a set of exceptions that can be thrown if a sleeping or blocked thread is interrupted.  They also offer a wait for operation called join that allows a parent thread to wait for the completion of the thread processing before continuing.
class Worker implements Runnable
{
    public void run()
    {
         // Work goes here
    }
}
 
class Parent
{
    static void main(String[] args)
    {
         // Create the worker object
         Worker w = new Worker();
         // Create the new thread
         Thread t = new Thread(w);
         // Start the thread
         t.start();
         // Wait for the thread to complete processing
         t.join();
    }
}

Critical Regions and Semaphores

When it is necessary to make execution a section of code mutually exclusive, Groovy offers the ability to surround that code in a lock block.  Locks apply to a specific object and when a second thread attempts to access the same block as another thread, it will wait until the lock has been released.
One of the simplest methods for managing access is by marking a method as synchronized or by the synchronized(this) {} semantic.  This locks access to the entire object, giving mutually exclusive access to the object and its memory structures to the requesting thread.  All other threads are placed in wait until that section has completed processing.
public class Example
{
    synchronized void mutexMethod()
    {
         // Only one thread can execute this code
         // at a time
    }
 
    void method()
    {
         synchronized(this)
         {
              // This code block is mutex
         }
    }
}
The synchronized block semantic described above does not apply only to the object it is defined in, it can also apply to any other objects available within the execution scope.
In addition to the synchronized mutex described above, Groovy also offers a Semaphore class which allows for limiting access to a set of code and resources not to a single thread alone, but to a pre-defined number of threads.  It follows the baseline pattern of initialize/wait/signal.  Creating a Semaphore object defines the number of permits, the acquire methods implements wait and signal and the release methods implement the relinquish.
Groovy also implements a number of atomic objects that manage mutex access to primitive values, such as integer or floating point, and offer synchronized methods to get and increment the value of that object.

Events

Groovy implements events via the base Object’s wait and notify methods.  A given thread can call the wait method which will place that thread in a wait state until the corresponding signal is received.  This signal is set by the Object’s notify method.
// Wait code in one thread
synchronized(obj)
{
    obj.wait()
}
 
// Send signal code in separate object
synchronized(obj)
{
     obj.notify()
}

No comments: