CS 245 Lab 11 – Threads
First, if you have checkpoints left over from last lab, get them inspected during the first 15 minutes of this lab. No credit will be awarded past these 15 minutes.
Don’t forget to work in pairs! Where possible, please work with someone that you did not work with last week. The exchange of new ideas and perspectives is not an opportunity you want to rob yourself of.
If you do not finish the checkpoints during this lab, you may complete them before next Monday and present them at 9:40 or 10:00 AM next Monday. The checkpoints must already have been completed; if you have questions, post on Piazza or visit office hours before Monday.
Synopsis
In this lab, you will look at breaking up a task into smaller subtasks that are completed concurrently. Instead of processing all pixels with just one processor core, you will assign to each processing core a portion of the pixels. All cores process instructions concurrently. If conditions are right, your task may be completed in a fraction of the time than it would with just one core.
Each chunk of work runs in a separate thread. The Java library provides a Thread
class that has two important methods: start
and join
. Method start
() schedules the thread to be run on a processor. The code that started the thread can wait for it to finish by calling join
(). To create the chunk of work, we write the code that does the work inside a class that implements the Runnable
interface and pass an instance of this worker class to the Thread
constructor.
An example application that demonstrates all of these points follows. Each of three threads sleeps for a random amount of time, but they all do so at the same time. Instead of the main thread waiting a.time
+ b.time
+ c.time
seconds, it just waits max(a.time, b.time, c.time)
seconds. Slick, huh?
public class ThreadSleep {
public static void main(String[] args) {
Thread a = new Thread(new Sleeper(0));
Thread b = new Thread(new Sleeper(1));
Thread c = new Thread(new Sleeper(2));
// Start the threads going concurrently.
a.start();
b.start();
c.start();
// Wait for them all to finish.
try {
a.join();
b.join();
c.join();
} catch (InterruptedException e) {
}
System.out.println("All threads have finished.");
}
private static class Sleeper implements Runnable {
private int threadIndex;
public Sleeper(int threadIndex) {
this.threadIndex = threadIndex;
}
@Override
public void run() {
int nMillis = (int) (Math.random() * 1000 * 20);
System.out.println("Sleeper " + threadIndex + " started and will sleep for " + nMillis / 1000.0 + " seconds.");
try {
Thread.sleep(nMillis);
} catch (InterruptedException e) {
// Sleep ended prematurely. Oh, well.
}
System.out.println("Sleeper " + threadIndex + " finished.");
}
}
}
Test this code out before moving on. You may use it as a model for your own code.
Checkpoint 1
Person A types.
Write a class that implements the Runnable
interface. Have its constructor accept parameters for two BufferedImage
s (one to read from, the other to write to), an int
thread index, and an int
thread count.
Your Runnable’s task is to process its portion of the input image, writing the results to its portion of the output image. This specification is not very exact on two points:
- What portion of the image is a
Runnable
‘s responsible for? There’s not one answer. If there aren
threads total, each thread should probably process1/n
pixels. Thread 0 might process the first 1/n pixels, thread 1 the second1/n
pixels, and so on. - What is the processing that a thread does? This is unspecified. You could lighten the image, negate the colors, swap a color channel, detect edges, and so on. Start with something simple, because the image processing isn’t the focus of this lab.
Checkpoint 2
Person B types.
Write a main method that implements the following algorithm:
read in input image
make identically-sized output image
make empty list of threads
for i up to nThreads
add new thread to list
start thread processing
nJoined = 0
while nJoined < nThreads
join on thread from list
if join successful
++nJoined
write out output image
The ImageIO
class is helpful here.
If you have time, you might be interested in timing the processing task with different numbers of threads. How does a large image processed by one thread compare to a large image processed by 10 threads?