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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值