Guarded Blocks 保护块

  线程经常需要协调它们的活动,最常用的协调方法是保护块。这样的块是以轮询一个条件开始,这个条件值必须是true,在块处理前。为了正确做到这样,有大量的步骤需要遵守。

  例如,guardedJoy 是一个方法,直到一个共享变量joy被另外一个线程设置了才会执行。这样的方法,理论上,简单的循环直到条件满足,但是了这样的循环是耗费的,因为它不断地在等待中执行。

  public void guardedJoy() {
    // Simple loop guard. Wastes
    // processor time. Don't do this!
    while(!joy) {}
    System.out.println("Joy has been achieved!");
  }

   一种更有效的保护是调用object.wait来暂停当前线程。wait的调用不会返回直到领一个线程已经发不了一个通知,一些特殊的事件可能发生。虽然不是这个线程正在等待的事件:
  
   public synchronized void guardedJoy() {
    // This guard only loops once for each special event, which may not
    // be the event we're waiting for.
    while(!joy) {
        try {
            wait();
        } catch (InterruptedException e) {}
    }
  

   注意:总是调用循环中的wait来测试正在等待的条件。不要假设中断(interrupt)是你正在等待的条件或者那个条件是true。

   像许多暂停执行的方法,wait会抛出InterruptedException。在这个例子中,我们能忽略这个异常,仅仅关注joy的值。

   为什么这个版本的guardedJoy是同步的?假设d是我们用来调用wait的对象。当一个线程调用d.wait,它必须拥有d的内在锁---否则将抛出一个错误。在一个同步方法中调用wait是一种简单的方式获取内在锁。

   当wait被调用,那个线程就释放锁并暂停执行。在不远的将来,另外一个线程将获取同样的锁并调用object.notifyAll,通知在这个锁上等待的所有线程,一些重要的事情发生了。
   public synchronized notifyJoy() {
    joy = true;
    notifyAll();
   }

    在第二个线程释放了锁后的一段时间,第一个线程重新获取锁并从wait的调用通过返回继续执行。

    注意:有第二个通知方法,notify,唤醒一个单一线程。因为notify不允许你指定要唤醒的线程,只有在大规模并行的应用程序中有用---即,程序中有大量的线程,所有的做那相似的工作。在这样的应用程序中,你不必在意哪个线程被唤醒。

    让我们使用保护块创建一个生产者--消费者应用程序。这类的应用在两个线程之间共享数据:生产者创建数据,消费者使用数据做一些事情。两个线程使用一个共享对象通信。协调就是必须的:在生产者传递数据前,消费者不必试着去获取它,并且生产者也不必试着去传递新的数据,如果消费者还没有获取旧的数据。

    在这个例子中,数据是一系列的文本信息,通过一个Drop的对象实现共享。

    public class Drop {
    // Message sent from producer
    // to consumer.
    private String message;
    // True if consumer should wait
    // for producer to send message,
    // false if producer should wait for
    // consumer to retrieve message.
    private boolean empty = true;

    public synchronized String take() {
        // Wait until message is
        // available.
        while (empty) {
            try {
                wait();
            } catch (InterruptedException e) {}
        }
        // Toggle status.
        empty = true;
        // Notify producer that
        // status has changed.
        notifyAll();
        return message;
    }

    public synchronized void put(String message) {
        // Wait until message has
        // been retrieved.
        while (!empty) {
            try {
                wait();
            } catch (InterruptedException e) {}
        }
        // Toggle status.
        empty = false;
        // Store message.
        this.message = message;
        // Notify consumer that status
        // has changed.
        notifyAll();
    }
  }


  生产者线程,用Producer定义,发送一系列相似的信息。字符串”DONE“表明所有的消息已经发送了。为了模仿现实世界中不可预知的性质,生产者在消息之间以随机间隔暂停。

  import java.util.Random;

public class Producer implements Runnable {
    private Drop drop;

    public Producer(Drop drop) {
        this.drop = drop;
    }

    public void run() {
        String importantInfo[] = {
            "Mares eat oats",
            "Does eat oats",
            "Little lambs eat ivy",
            "A kid will eat ivy too"
        };
        Random random = new Random();

        for (int i = 0;
             i < importantInfo.length;
             i++) {
            drop.put(importantInfo[i]);
            try {
                Thread.sleep(random.nextInt(5000));
            } catch (InterruptedException e) {}
        }
        drop.put("DONE");
    }
}

消费者线程,以Consumer定义,简单地接受消息并打印出来,直到获取了”DONE“字符串。线程也以随机间隔暂停

  import java.util.Random;

public class Consumer implements Runnable {
    private Drop drop;

    public Consumer(Drop drop) {
        this.drop = drop;
    }

    public void run() {
        Random random = new Random();
        for (String message = drop.take();
             ! message.equals("DONE");
             message = drop.take()) {
            System.out.format("MESSAGE RECEIVED: %s%n", message);
            try {
                Thread.sleep(random.nextInt(5000));
            } catch (InterruptedException e) {}
        }
    }
}
 
  最后,这是主线程,以ProducerConsumerExample 定义。启动了生产者和消费者线程。
 
  public class ProducerConsumerExample {
    public static void main(String[] args) {
        Drop drop = new Drop();
        (new Thread(new Producer(drop))).start();
        (new Thread(new Consumer(drop))).start();
    }
}

  注意: The Drop class was written in order to demonstrate guarded     blocks. To avoid re-inventing the wheel, examine the  existing data structures in the Java Collections Framework before    trying to code your own data-sharing objects. For more information,  refer to the Questions and Exercises section.



你可能感兴趣的:(Guarded Blocks)