teaching machines

CS 245 Lab 11 – Threads

April 23, 2014 by . Filed under cs245, labs, spring 2014.

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 BufferedImages (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:

  1. What portion of the image is a Runnable‘s responsible for? There’s not one answer. If there are n threads total, each thread should probably process 1/n pixels. Thread 0 might process the first 1/n pixels, thread 1 the second 1/n pixels, and so on.
  2. 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?