Java IoT Authors: Elizabeth White, Liz McMillan, Zakia Bouachraoui, Yeshim Deniz, 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
    Early Bird Registration Discount Expires on August 31, 2018 Conference Registration Link ▸ HERE. Pick from all 200 sessions in all 10 tracks, plus 22 Keynotes & General Sessions! Lunch is served two days. EXPIRES AUGUST 31, 2018. Ticket prices: ($1,295-Aug 31) ($1,495-Oct 31) ($1,995-Nov 12) ($2,500-Walk-in)
    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...
    Nicolas Fierro is CEO of MIMIR Blockchain Solutions. He is a programmer, technologist, and operations dev who has worked with Ethereum and blockchain since 2014. His knowledge in blockchain dates to when he performed dev ops services to the Ethereum Foundation as one the privileged few developers to work with the original core team in Switzerland.
    René Bostic is the Technical VP of the IBM Cloud Unit in North America. Enjoying her career with IBM during the modern millennial technological era, she is an expert in cloud computing, DevOps and emerging cloud technologies such as Blockchain. Her strengths and core competencies include a proven record of accomplishments in consensus building at all levels to assess, plan, and implement enterprise and cloud computing solutions. René is a member of the Society of Women Engineers (SWE) and a m...
    Digital Transformation and Disruption, Amazon Style - What You Can Learn. Chris Kocher is a co-founder of Grey Heron, a management and strategic marketing consulting firm. He has 25+ years in both strategic and hands-on operating experience helping executives and investors build revenues and shareholder value. He has consulted with over 130 companies on innovating with new business models, product strategies and monetization. Chris has held management positions at HP and Symantec in addition to ...
    The challenges of aggregating data from consumer-oriented devices, such as wearable technologies and smart thermostats, are fairly well-understood. However, there are a new set of challenges for IoT devices that generate megabytes or gigabytes of data per second. Certainly, the infrastructure will have to change, as those volumes of data will likely overwhelm the available bandwidth for aggregating the data into a central repository. Ochandarena discusses a whole new way to think about your next...
    CloudEXPO | DevOpsSUMMIT | DXWorldEXPO 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.
    Dynatrace is an application performance management software company with products for the information technology departments and digital business owners of medium and large businesses. Building the Future of Monitoring with Artificial Intelligence. Today we can collect lots and lots of performance data. We build beautiful dashboards and even have fancy query languages to access and transform the data. Still performance data is a secret language only a couple of people understand. The more busine...
    All in Mobile is a place where we continually maximize their impact by fostering understanding, empathy, insights, creativity and joy. They believe that a truly useful and desirable mobile app doesn't need the brightest idea or the most advanced technology. A great product begins with understanding people. It's easy to think that customers will love your app, but can you justify it? They make sure your final app is something that users truly want and need. The only way to do this is by ...
    DXWorldEXPO LLC announced today that Big Data Federation to Exhibit at the 22nd International CloudEXPO, colocated with DevOpsSUMMIT and DXWorldEXPO, November 12-13, 2018 in New York City. Big Data Federation, Inc. develops and applies artificial intelligence to predict financial and economic events that matter. The company uncovers patterns and precise drivers of performance and outcomes with the aid of machine-learning algorithms, big data, and fundamental analysis. Their products are deployed...