15. Multithreading

About this Tutorial –

Objectives –

This course is aimed at object-oriented developers (e.g. C++ or C#) who need to transition into Java. It is also aimed at those learning to program for the first time; the course covers the Java programming constructs and APIs quickly, focussing on the differences between Java and other OO languages.

Audience

This training course is aimed at OO developers who need to transition into Java.

Prerequisites

No previous experience in Java programming is required. But any experience you do have in programming will help. Also no experience in eclipse is required. But again any experience you do have with programming development environments will be a valuable.

Contents

The Java course cover these topics and more:

  • Flow Control: Decision making: if and if-else; The switch statement; Looping: for loops; while loops; do-while loops; for-each style loops; Assertionsv
  • Concurrency: Overview of multithreading; Creating new threads; Object locking; Using wait, notify, and notifyAll
  • Collections: Overview of Java SE collection classes; Generics; Using List-based collection classes; Using Set-based collection classes; Using Map-based collection classes; Collection techniques

Exam Preparation

The Java course will help you prepare for these certifications:

  • Oracle Certified Java Associate – Exam 1Z0-803
  • Oracle Certified Java Professional – Exam 1Z0-804

Download Solutions

HTML tutorial


Overview

Estimated Time – 1 Hour

Not what you are looking? Try the next tutorial – Java Swing

Lab 1: Creating multiple threads

Lab 1: Creating multiple threads
  1. Overview
    • Java offers two ways to create another thread:
    • You can either implement the Runnable interface:
      • Defines a single method, run()
      • You must implement run(), to specify the work you want to do in the separate thread
    • Or you can extend the Thread class:
      • Also has a run() method
      • You can override run(), to specify the work you want to do in the separate thread
    • Either way, when the run() method terminates:
      • – that’s the end of the thread
  2. Implementing the Runnable Interface
    • This class finds all prime numbers in a specified range
      • This is a time-consuming task, so we do it in a separate thread (i.e. in the run() method)
        public class PrimeNumberFinder implements Runnable {
         private int from, to;
         private List primes;
         public PrimeNumberFinder(int from, int to) {
          this.from = from;
          this.to = to;
          this.primes = new ArrayList();
         }
         public void run() {
          for (int number = from; number <= to; number++)    if (isPrime(number)) primes.add(number);  }  private boolean isPrime(int number) {   for (int i = 2; i < number; i++)    if (number % i == 0) return false;   return true;  } }
      • View code file.
  3. The Thread Class
    • Thread has many instance methods to manage threads:
      • void start(Runnable runnableObject)
      • void run()
      • Thread.State getState()
      • int getPriority()
      • ...
    • Thread also has some useful static methods:
      • static Thread currentThread()
      • static void sleep(int ms)
      • static void yield()
      • ...
  4. Starting a New Thread
    • You can create and start a new thread as follows:
      • Create a Runnable object
      • Create a Thread object
      • Call the Thread object's start() method, passing the runnable object as a parameter
        private static void demoRunnableImplementation() {
         System.out.print("Enter 'from': ");
         int from = scanner.nextInt();
         System.out.print("Enter 'to': ");
         int to = scanner.nextInt();
         PrimeNumberFinder finder = new PrimeNumberFinder(from, to);
         Thread backgroundThread = new Thread(finder);
         backgroundThread.start();
         ...
        }
      • View code file.
  5. Coordinating Threads
    • The original thread can continue to do work while other threads execute
    • If you want to wait for the other thread to finish, you can call the join() method
      // Do some work on the main thread, while we're waiting...
      // ...
      // Now let's wait for the other thread to finish (can specify a max wait time here).
      try {
       backgroundThread.join();
      }
      catch (InterruptedException ex) {}
      // Get the results from the background thread.
      List primes = finder.getPrimes();
      System.out.println("Prime numbers: " + primes);
  6. Subclassing the Thread class
    • The other way to do multithreading is to subclass the Thread class:
    • Define a class that subclasses Thread
      • Define any constructor parameters as needed, to initialize state
      • Override run(), and put your background-thread -code here
      • Example: see PrimeNumberFinderThread.java
    • Then in your client code:
      • Create an instance of your Thread subclass
      • Call the object's start() method
      • This will cause the thread's own run() method to be called
      • Example: see Main.java, demoThreadSubclass() method
Lab
  1. Introduce multithreading into the application
    • The application is unacceptable because it forces the user to wait until a search finishes before the user can do anything else. A better approach would be to use multithreading
    • To run code in a separate thread, you must define a class that implements the Runnable interface. So, add a new class named DirectorySearcher and implement it as follows:
      • The class must implement the Runnable interface (obviously)
        public class DirectorySearcher implements Runnable { ... }
      • View code file.
      • The class needs some instance variables so it can remember what it's meant to be doing. We suggest the following instance variables:
        • A String instance variable called directoryName, which remembers the name of the directory to search
          private String directoryName;
        • View code file.
        • A List<File> instance variable called thisResult, to accumulate all the files and sub-directories for this search. Create an empty list initially
          private List<File> thisResult = new ArrayList<File>();
        • View code file.
        • A Map<String, List<File>> instance variable called allResults, which will hold the results of all searches completed so far (this will be passed in from the main code - see the constructor info below)
          private Map<String, List<File>> allResults;
        • View code file.
      • Write a constructor. The constructor requires two parameters, which will be passed in from the main application code:
        • The name of the directory to search
        • The map into which the thread can store its results on completion of this search
          public DirectorySearcher(String directoryName, Map> allResults) {
           this.directoryName = directoryName;
           this.allResults = allResults;
          }
        • View code file.
      • Copy the searchDirectory() method from the Main class into the DirectorySearcher class (because the search operation will now be performed in your background thread class). Refactor the method as follows:
        • It doesn't need to be static any more (it was only declared static originally because all the methods in the Main class were static for simplicity).
        • It doesn't need a List<String> parameter (the DirectorySearcher class can use the thisResult instance variable instead).
           // Recursive method, to search a directory for all its entries (files and sub-directories).
           private void searchDirectory(File directory) {
            // Loop through directory, to find all files and sub-directories.
            for (File fileEntry : directory.listFiles()) {
             // Add the file entry to this result.
             thisResult.add(fileEntry); 
             // If the file entry is a sub-directory, search it recursively.
             if (fileEntry.isDirectory()) {
              searchDirectory(fileEntry); 
             }
            }
           }
        • View code file.
      • Write a run() method to perform the work for this thread. Implement it as follows:
        • Invoke searchDirectory(), passing in a File object to represent the directory to search
        • After searchDirectory() has completed, insert the result into the map of all results (similar to the existing code in the Main class's doSearch() method)
           // This method will execute in a different thread.
           public void run() {
            
            // Search the directory (and all its sub-directories).
            searchDirectory(new File(directoryName));
            
            // When we're done, add this result to the "allResults" collection.
            allResults.put(directoryName, thisResult);
           }
        • View code file.
      • In the Main class, refactor doSearch() to perform the search in a background thread (i.e. create a DirectorySearcher object and start it in a separate thread).
        // Start a directory search in another thread.
        DirectorySearcher searcher = new DirectorySearcher(directoryName, allResults);
        Thread thread = new Thread(searcher);
        thread.start();
        System.out.println("Started search of " + directoryName);
      • View code file.
    • Run the application again. Now, when you do a search, the search should take place in a background thread. This means you can kick off multiple searches simultaneously

Lab 2: Synchronizing threads

Lab 2: Synchronizing threads
  1. Overview
    • In a multithreaded Java application:
      • Multiple threads might be accessing the same object "at the same time"
      • This can cause inconsistencies to occur, due to conflicting interleaved updates on the object
    • To avoid these problems, you should synchronize access to the object
      • By using the synchronized keyword
  2. Defining Synchronized Methods
    • You can apply the synchronized keyword to methods
      public class BankAccount {
       public synchronized double deposit(amount) {
        balance += amount;
        return balance;
       }
       public synchronized double withdraw(amount) {
        balance -= amount;
        return balance;
       }
       ...
  3. Defining Synchronized Blocks
    • You can also apply the synchronized keyword to blocks
      public class BankAccount {
       List transactions = new ArrayList();
       public void processTransactions() {
        ...
        synchronized (transactions) {
         // We have thread-safe access to transactions list in here
         //
        }
        ...
       }
    • What are the benefits of synchronizing on a block, rather than on a method?
  4. Waiting for Other Threads
    • The Object class defines 3 methods that allow threads to co-operatively wait for each other:
      • Note: You can only call these methods in a synchronization scope
    • wait()
      • Tells the calling thread to give up the monitor and go to sleep...
      • Until another thread enters the same monitor and calls notify()
    • notify()
      • Wakes up the first thread that called wait() on the same object
    • notifyAll()
      • Wakes up all the threads that called wait() on the same object
      • The highest priority thread will run first
Lab
  1. Ensure thread safety
    • The application isn't thread-safe at the moment, because multiple threads might insert results into the allResults map simultaneously. As currently implemented, the application uses a HashMap, which is a non-thread-safe collection class.
    • It's extremely important to ensure thread safety in your applications. There are various ways to achieve thread safety in this situation - the simplest approach is to use a thread-safe map class, e.g. ConcurrentHashMap. Refactor the Main class to do this.
      // This map will hold the result of all directory scans.
      private static Map<String, List<File>> allResults = new ConcurrentHashMap<>();
    • View code file.
    • You probably won't observe any differences when you run the application, but at least you won't get any nasty thread concurrency surprises later on either!

Lab 3: Synchronization classes

Lab 3: Synchronization classes
  1. Using Semaphores
    • Semaphore:
      • Allows counted number of threads to access a resource concurrently
    • To acquire one or more permit(s):
      • Call acquire(), blocks until permit(s) available
      • Decrements the number of permits available
    • To release one or more permit(s):
      • Call release()
      • Increments the number of permits available
    • Additional capabilities:
      • tryAcquire(), availablePermits(), drainPermits(), reducePermits()
  2. Using Latches
    • CountDownLatch:
      • Is initialized with a given count
      • Causes threads to wait until the count reaches zero (one-shot)
      • Allows a coordinating thread to subdivide work across several threads, and wait until they have all completed
    • To wait on a CountDownLatch:
      • Call await(), blocks until the latch's count reaches zero
    • To signal a CountDownLatch:
      • Call countDown()
      • When count reaches zero, all threads waiting on latch are released
      • Any subsequent await() calls on the latch continue unhindered
  3. Using Barriers
    • CyclicBarrier:
      • Allows several threads to wait for each other to reach a common barrier point
      • Can be reset after it's fired (hence the term "cyclic barrier")
    • A CyclicBarrier supports an optional Runnable command
      • Run once per barrier point, after the last thread arrives (but before any thread has been released)
      • Useful for updating shared state before any parties continue
    • To await all parties' arrival at a barrier:
      • Call await(), with an optional timeout
  4. Using Exchangers
    • Exchanger<V>:
      • Allows threads to swap elements within pairs
      • Effectively, a bidirectional form of SynchronousQueue
      • Useful in pipeline-based solutions
    • To exchange a value using an Exchanger<V>:
      • Call exchange(v), to wait for another thread to arrive at this execution point
      • Causes your specified value to be transferred to other thread
      • - and you receive the other thread's object in exchange
Lab
  1. Wait for threads to terminate gracefully
    • Improve the application so that it allows all running threads to complete first when the application shuts down. Hints:
      • Keep a list of all threads you create in the application.
         // This list will hold all the threads that have been started (so we can wait for them to finish when we quit).
         private static List<Thread> allThreads = new ArrayList<>();
        // Add the new thread to the list of all threads (so we can wait for it to finish when we quit).
        allThreads.add(thread);
      • View code file.
      • When the user quits the application, loop through the thread list and invoke join() to wait for the thread to finish. Optionally, specify a timeout on each join() operation (to avoid potentially having to wait a very long time for a thread to finish!)
         // Tidy-up before the application quits.
         private static void doQuit() {
          // Give each thread 3 seconds to finish.
          System.out.println("Giving each thread 3 seconds to finish...");
          for (Thread thread: allThreads) {
           try {
            thread.join(3000);
           } catch (InterruptedException ex) {}
          }
         }
      • View code file.

 

Well done. You have completed the tutorial in the Java course. The next tutorial is

16. Java Swing


Back to beginning
Copyright © 2016 TalkIT®






If you liked this post, please comment with your suggestions to help others.
If you would like to see more content like this in the future, please fill-in our quick survey.
Scroll to Top