In this tutorial you will learn more Java and will look at multi-threading in Java. This will allow you to write a simple Java app that you can then use in your IDE
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.
Experience using a contemporary OO language such as C++ or C# would be useful but is not required.
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:
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
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;
}
}
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();
...
}
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);
Subclassing the Thread class
The other way to do multithreading is to subclass the Thread class:
This will cause the thread's own run() method to be called
Example: see Main.java, demoThreadSubclass() method
Lab
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 { ... }
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>();
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;
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;
}
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);
}
}
}
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);
}
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);
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
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;
}
...
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?
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
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 collectionclass.
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<>();
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!
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
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
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
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);
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) {}
}
}
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.
Manage cookie consent
You can view this website without consenting to extra cookies. However you will need to accept the 'marketing' cookies to send messages via the contact forms & see any maps displayed on the site
Functional
Always active
Cookies necessary for the website to work.
Preferences
The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.
Statistics
The technical storage or access that is used exclusively for statistical purposes.Cookies used to track user interaction on the site, which helps us to evaluate & improve the website.
Marketing: Forms & additional content (Marketing)
We need your permission to place ‘marketing’ cookies, so you are able to use the contact forms & see embedded content e.g. videos and maps. - - - - We have added protection to our contact forms to help prove that a human (rather than a spambot) is filling
If you would like to see more content like this in the future, please fill-in our quick survey.