Java IoT Authors: Elizabeth White, Liz McMillan, Yeshim Deniz, Zakia Bouachraoui, Pat Romanski

Related Topics: Java IoT

Java IoT: Article

Making Threads Flexible

Making Threads Flexible

If you are experienced in using the POSIX threads package (pthread) in C or C++, one of the first things you may have noticed while learning Java is that thread semantics is one area where Java's designers did not try to emulate C semantics. The next thing you may have noticed is that the Java approach seems awkward in many circumstances.

For instance, consider the timer() method shown in Listing 1. You might want to add a method like this to a long-running application to reassure yourself that your Java Virtual Machine has not gotten hung. Since this method contains an infinite loop, it only makes sense to run it in its own thread. The POSIX threads model allows you to pass a function pointer and an argument pointer to the pthread_create() method. The specified function is executed in its own thread of control and the argument pointer can be used to pass data structures into or out of the method. If you are used to programming with this threads model, you would expect to be able to invoke the timer() method using a construct like:

new Thread(this.timer(5)).start();

Unfortunately, Java's semantics do not support this. Instead, to start a thread in Java, your class must implement the Runnable interface. (A similar option, which in the interest of clarity we will not discuss, is to extend the Thread class.) You can then pass an instance of your Runnable class to the Thread constructor. Calling the resulting Thread object's start() method will cause the run() method of your object to be invoked in its own execution thread. The Runnable interface does not allow the run() method to return a result, take any parameters or throw any exceptions. The run() method must also be public to satisfy the Runnable interface. With these semantics in mind, the AppTimer class of Listing 2 shows a naive solution to the problem of invoking the timer() method in a separate thread and passing it a parameter.

The programming approach (or "design pattern") illustrated by Listing 2 is simple. When you have a method you wish to execute in a thread, do the following:

  • Change your class definition to include "implements Runnable."
  • Add a public run() method to your class that returns void, takes no parameters and throws no exceptions.
  • At the place in your code where you want to start the method, put its parameters in instance variables, pass the "this" reference to the Thread constructor and call start() on the new Thread.
  • Code your run() method to invoke the target method and pass it the instance variables [or move the code from the target method into run()].

    This design pattern does get the job done. That is, the AppTimer class in Listing 2 is successful in running timer(periodLength) in its own thread. In spite of this, the approach is unappealing for several reasons. The main drawback is that it forces you to expose a public run() method that is not really intended for use by clients. Someone looking at Listing 2 could easily be confused about how AppTimer's designer expected the class to be used. If someone mistakenly passes an instance of this class to a Thread constructor and starts it, it will function incorrectly. However, that type of usage is normally inferred by implementing Runnable(). There are also other problems with this approach. It is difficult to use in classes that already expose a public run() method for client use, and it is awkward if a class contains several thread driven-methods.

    A more elegant AppTimer implementation, using a design pattern that overcomes these shortcomings, is shown in Listing 3. It defines an inner helper class (TimerHelper) that hides the run() method. An inner class is a class that is defined inside a top-level class as a member of the top-level class. As long as the inner class and its constructors are not public, instances cannot be created outside its defining scope and it is not part of the interface of the top-level class.

    Using inner classes to manage threads within a class is an attractive alternative to the approach taken in Listing 2. Not only does it relieve the top-level class of having to implement Runnable and expose a spurious and confusing public run() method, but it also imparts considerable flexibility. The helper class associated with any method can be customized to provide a variety of services such as facilities to stop the method, query its progress or retrieve its result.

    The design pattern of Listing 3 generalizes as follows:

  • If you have a class, , with a method, <()> that you want to run in its own thread of control, create an inner class within named Helper which implements Runnable.
  • Create a constructor for Helper with the signature Helper (, ).
  • Code the constructor to place its parameters in instance variables, construct a Thread passing the "this" reference and call start() on the new Thread Code Helper's run() method to pass the parameters saved by the constructor to the top-level class's .
  • At the place in the code where you wish to start , construct a new Helper(this,).

    While this approach is a considerable improvement compared to trying to place all of a top-level class's thread management logic in its run() method, it still has some drawbacks. The main problem is that it may lead to top-level classes that are littered with inner helper classes. Since all the helper classes have similar logic, the code can become tedious to develop, read and maintain.

    Fortunately, you can use the capabilities provided by Java's Reflection API (introduced in JDK 1.1) to implement a single class that performs all the thread management functions of this design pattern. The Reflection API allows a method name and signature (represented as a String and an array of Class objects) to be passed among methods. Code receiving such information can determine, at runtime, if a particular class has a method with the specified name and signature, and invoke it if so. These facilities are used in the ThreadHelper class shown in Listing 4 and the much simplified AppTimer implementation of Listing 5 to launch a thread into the timer() method.

    The ThreadHelper constructor receives a reference to an Object that contains a method to be executed in an independent thread, the name of the target method and an Object array containing the parameters to be sent to the method. By calling the getClass() method on each parameter [getClass() is inherited by all classes from Object], ThreadHelper builds an array of Class objects that identifies the signature of the target method. Calling getDeclaredMethod() on the target object's class and passing the target method's name and signature returns the Method object for the target method. The Method object is passed to ThreadHelper's run() method, along with its parameters, in instance variables. When ThreadHelper passes its "this" reference to the Thread constructor and calls start() on the new Thread, its run() method is started in a new thread of control. The run() method retrieves the target method's Method object and parameter and invokes the target method.

    Because ThreadHelper is intended for general use, it should provide more function than the specialized TimerHelper of Listing 3. Specifically, it needs to allow its client to retrieve any result that might be returned by the target method and to allow the client to (optionally) block and await the completion of the method. These capabilities are provided by ThreadHelper's join() method. This join() method is equivalent to the join() function provided in C by the POSIX pthread package. The boolean completed variable is used by the join() function as a synchronization semaphore. If completed has not been set, join() uses the wait() function to block. When the target method returns, run() calls signalCompletion() to set the semaphore and use notifyAll() to wake up any threads that might be waiting in join(). These actions should be performed only in a "synchronized" method, which is why they are not simply performed in run().

    It is also possible, of course, to add a variety of useful methods to ThreadHelper. Some functions like isComplete() or kill() are trivial to add. Others like setTimeOut() are more difficult. The ThreadHelper class shown in Listing 4 illustrates the basic concepts involved in implementing a general helper class to encapsulate thread management primitives. This technique can be used to relieve other classes from having to implement Runnable or use inner classes to exploit Java's powerful multithreading capabilities.

    ThreadHelper does have one subtle but significant limitation. The parameters passed to methods by way of the ThreadHelper constructor must be classes rather than Java primitive types. That is, the timer() method of Listing 1 must be modified to take an input parameter of type Integer rather than int. This allows the version of AppTimer shown in Listing 5 to pass the input parameter as "new Integer(periodLength)" rather than simply periodLength. This limitation comes from ThreadHelper calling getClass() for each item in the parameter array. Primitive types (like int) don't inherit from Object and therefore do not have a getClass() method.

    The ThreadHelper class is more complex than TimerHelper (see Listing 3), and if you rarely use thread-driven logic, then occasionally incorporating it into specialized inner classes may be adequate. However, the Reflection API makes it possible to provide a general solution with only slightly more effort.

  • More Stories By Philip Rousselle

    Philip Rousselle develops systems and network management software for Tivoli Systems. He is currently designing techniques for efficiently communicating event data (alerts) over large distributed systems. He is also investigating how network topology information can be used to enhance systems management software.

    More Stories By Mike McNally

    Mike McNally is the lead designer of distributed monitoring products for Tivoli Systems in Austin, TX.

    Comments (0)

    Share your thoughts on this story.

    Add your comment
    You must be signed in to add a comment. Sign-in | Register

    In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.

    IoT & Smart Cities Stories
    IoT is rapidly becoming mainstream as more and more investments are made into the platforms and technology. As this movement continues to expand and gain momentum it creates a massive wall of noise that can be difficult to sift through. Unfortunately, this inevitably makes IoT less approachable for people to get started with and can hamper efforts to integrate this key technology into your own portfolio. There are so many connected products already in place today with many hundreds more on the h...
    SYS-CON Events announced today that IoT Global Network has been named “Media Sponsor” of SYS-CON's @ThingsExpo, which will take place on June 6–8, 2017, at the Javits Center in New York City, NY. The IoT Global Network is a platform where you can connect with industry experts and network across the IoT community to build the successful IoT business of the future.
    CloudEXPO New York 2018, colocated with DXWorldEXPO New York 2018 will be held November 11-13, 2018, in New York City and will bring together Cloud Computing, FinTech and Blockchain, Digital Transformation, Big Data, Internet of Things, DevOps, AI, Machine Learning and WebRTC to one location.
    Andrew Keys is Co-Founder of ConsenSys Enterprise. He comes to ConsenSys Enterprise with capital markets, technology and entrepreneurial experience. Previously, he worked for UBS investment bank in equities analysis. Later, he was responsible for the creation and distribution of life settlement products to hedge funds and investment banks. After, he co-founded a revenue cycle management company where he learned about Bitcoin and eventually Ethereal. Andrew's role at ConsenSys Enterprise is a mul...
    DXWorldEXPO | CloudEXPO are the world's most influential, independent events where Cloud Computing was coined and where technology buyers and vendors meet to experience and discuss the big picture of Digital Transformation and all of the strategies, tactics, and tools they need to realize their goals. Sponsors of DXWorldEXPO | CloudEXPO benefit from unmatched branding, profile building and lead generation opportunities.
    Disruption, Innovation, Artificial Intelligence and Machine Learning, Leadership and Management hear these words all day every day... lofty goals but how do we make it real? Add to that, that simply put, people don't like change. But what if we could implement and utilize these enterprise tools in a fast and "Non-Disruptive" way, enabling us to glean insights about our business, identify and reduce exposure, risk and liability, and secure business continuity?
    The best way to leverage your Cloud Expo presence as a sponsor and exhibitor is to plan your news announcements around our events. The press covering Cloud Expo and @ThingsExpo will have access to these releases and will amplify your news announcements. More than two dozen Cloud companies either set deals at our shows or have announced their mergers and acquisitions at Cloud Expo. Product announcements during our show provide your company with the most reach through our targeted audiences.
    DXWorldEXPO LLC announced today that Telecom Reseller has been named "Media Sponsor" of CloudEXPO | DXWorldEXPO 2018 New York, which will take place on November 11-13, 2018 in New York City, NY. Telecom Reseller reports on Unified Communications, UCaaS, BPaaS for enterprise and SMBs. They report extensively on both customer premises based solutions such as IP-PBX as well as cloud based and hosted platforms.
    To Really Work for Enterprises, MultiCloud Adoption Requires Far Better and Inclusive Cloud Monitoring and Cost Management … But How? Overwhelmingly, even as enterprises have adopted cloud computing and are expanding to multi-cloud computing, IT leaders remain concerned about how to monitor, manage and control costs across hybrid and multi-cloud deployments. It’s clear that traditional IT monitoring and management approaches, designed after all for on-premises data centers, are falling short in ...
    The deluge of IoT sensor data collected from connected devices and the powerful AI required to make that data actionable are giving rise to a hybrid ecosystem in which cloud, on-prem and edge processes become interweaved. Attendees will learn how emerging composable infrastructure solutions deliver the adaptive architecture needed to manage this new data reality. Machine learning algorithms can better anticipate data storms and automate resources to support surges, including fully scalable GPU-c...