0

Suppose we have a thread-safe class called Teacher which implements Runnable. A teacher can either read or write to a student's book.

public class Teacher implements Runnable {

boolean doneWithBook = false;
private Lock lock = new ReentrantLock();
private Condition cond = lock.newCondition();

public void readBook(Book book) {

    lock.lock();
    try {
        book.read();
        doneWithBook = false;
        cond.signalAll();
        System.out.println("Teacher read the book");
    } finally {
        lock.unlock();
    }
}

public void writeToBook(Book book) {

    lock.lock();
    try {
        book.write();
        doneWithBook = true;
        System.out.println("Teacher wrote to book.");
    } finally {
        lock.unlock();
    }
}

Teacher implements Runnable, so the task can be ran on its own separate thread. What I don't understand is what to put inside the Runnable's interface run() method. What if I want to read/write to the book. How does run() come into play? Examples are greatly appreciated.

@Override
public void run() {

  // now what???

}
Gregg1989
  • 675
  • 6
  • 14
  • 24
  • Wouldn't you want separate reader and writer threads? – C.B. Mar 24 '14 at 15:56
  • Why do you think that teacher needs to implement `Runnable` at all? – Nick Holt Mar 24 '14 at 15:57
  • @C.B. What would be the difference between separate threads for reading and writing, compared to doing it in the same class? – Gregg1989 Mar 24 '14 at 15:57
  • @NickHolt I want to run this on a separate thread, not the main one. How can I run it on a separate thread if it does not implement Runnable or Callable? – Gregg1989 Mar 24 '14 at 15:59
  • If you are going to have one class that can read or write, you would need to allow a global lock that all classes that can non-deterministically read/write a book, rather than a lock local to the class that wants to read/write. – C.B. Mar 24 '14 at 16:03
  • @C.B. Can you please provide a detailed response as an answer to this question? This sounds interesting and I'm not familiar with it. I'm still new to concurrency and I find it hard to understand. – Gregg1989 Mar 24 '14 at 16:07

2 Answers2

2

OK, in general terms it's your task that implements Runnable, so in your case you might have the following:

public ReadTask implements Runnable
{
  private Teacher teacher;

  public ReadTask(Teacher teacher)
  {
    this.teacher = teacher;
  }

  public void run()
  {
    teacher.readBook();
  }
}

public WriteTask implements Runnable
{
  private Teacher teacher;

  public WriteTask (Teacher teacher)
  {
    this.teacher = teacher;
  }

  public void run()
  {
    teacher.writeToBook();
  }
}

Then you calling code would look like this:

Teacher teacher = ...
new Thread(new ReadTask(teacher)).start();
new Thread(new WriteTask(teacher)).start();

Also note, the Condition in your code isn't really achieving anything.

Nick Holt
  • 33,455
  • 4
  • 52
  • 58
  • Good point about `Condition`. How do you suggest I use Condition in my code? – Gregg1989 Mar 24 '14 at 17:01
  • You don't need to use a `Condition`, they're used when you want a thread to *wait* for another thread to finish. The running thread calls `signal` or `signalAll` to wake the *waiting* thread(s) – Nick Holt Mar 24 '14 at 17:04
  • I mean, if I were to use Condition, what scenario would I need to have to use Condition? I don't understand why a thread would ever need to wait for another thread to finish, given that each thread locks the object and releases the lock in the finally clause. Other threads will wait for the previous thread to finish either way, right? – Gregg1989 Mar 24 '14 at 17:11
  • There's some examples of `wait` `notify`, which achieves much the same thing as a `Condition`, here - http://stackoverflow.com/questions/2536692/a-simple-scenario-using-wait-and-notify-in-java – Nick Holt Mar 24 '14 at 17:33
1

What if I want to read/write to the book. How does run() come into play?

So there are a couple ways to do this. The easiest is to have in the run() method have an if based on some sort of constructor argument:

private final boolean reading;
public Teacher(boolean reading) {
    this.reading = reading;
}
public void run() {
    if (reading) {
       readBook(book);
    } else {
       writeToBook(book);
    }
}

However, if the reading and writing code does not share any code then I would have a ReadingTeacher class and a WritingTeacher class that do different things. So then you'd do something like:

public class ReadingTeacher implements Runnable {
    ...
    public void run() {
        readBook(book);
    }
}

All this said, I believe your problem is with the conditional. If you have 2 different Runnable objects, they need to share the same conditional. Te conditional should be created outside of the threads and passed into the constructor. Then the reading teacher can signal on the same conditional object that the writing teacher is waiting on.

 public class Teacher implements Runnable {
      private final boolean reading;
      private final Condition cond;
      public Teacher(boolean reading, Condition cond) {
          this.reading = reading;
          this.cond = cond;
      }
      public void run() {
          if (reading) {
              readBook(book);
          } else {
              writeToBook(book);
          }
      }
 }
 ...
 Condition cond = lock.newCondition();
 new Thread(new Teacher(true, cond)).start();
 new Thread(new Teacher(false, cond)).start();

A more simplified solution would be to make Condition cond be static so all instances share the same condition although that's not as good a pattern.

Gray
  • 115,027
  • 24
  • 293
  • 354