<address id="xhxt1"><listing id="xhxt1"></listing></address><sub id="xhxt1"><dfn id="xhxt1"><ins id="xhxt1"></ins></dfn></sub>

    <thead id="xhxt1"><dfn id="xhxt1"><ins id="xhxt1"></ins></dfn></thead>

    基本线程同步(四)在同步代码中使用条件

    声明:本文是《 Java 7 Concurrency Cookbook 》的第二章,作者: Javier Fernández González ? ? 译者:许巧辉 校对:方腾飞

    在同步代码中使用条件

    在并发编程中的一个经典问题是生产者与消费者问题,我们有一个数据缓冲区,一个或多个数据的生产者在缓冲区存储数据,而一个或多个数据的消费者,把数据从缓冲区取出。

    由于缓冲区是一个共享的数据结构,我们必须采用同步机制,比如synchronized关键字来控制对它的访问。但是我们有更多的限制因素,如果缓冲区是满的,生产者不能存储数据,如果缓冲区是空的,消费者不能取出数据。

    对于这些类型的情况,Java在Object对象中提供wait(),notify(),和notifyAll() 方法的实现。一个线程可以在synchronized代码块中调用wait()方法。如果在synchronized代码块外部调用wait()方法,JVM会抛出IllegalMonitorStateException异常。当线程调用wait()方法,JVM让这个线程睡眠,并且释放控制 synchronized代码块的对象,这样,虽然它正在执行但允许其他线程执行由该对象?;さ钠渌鹲ynchronized代码块。为了唤醒线程,你必 须在由相同对象?;さ膕ynchronized代码块中调用notify()或notifyAll()方法。

    在这个指南中,你将学习如何通过使用synchronized关键字和wait()和notify(),notifyAll()方法实现生产者消费者问题。

    准备工作

    这个指南的例子使用Eclipse IDE实现。如果你使用Eclipse或其他IDE,如NetBeans,打开它并创建一个新的Java项目。

    如何做…

    按以下步骤来实现的这个例子:

    1.创建EventStorage类,包括一个名为maxSize,类型为int的属性和一个名为storage,类型为LinkedList<Date>的属性。

    
    public class EventStorage {
    private int maxSize;
    private List<Date> storage;
    
    

    2.实现这个类的构造器,初始化所有属性。

    
    public EventStorage(){
    maxSize=10;
    storage=new LinkedList<>();
    }
    
    

    3. 实现synchronized方法set(),用来在storage上存储一个事件。首先,检查storage是否已满。如果满了,调用wait()方 法,直到storage有空的空间。在方法的尾部,我们调用notifyAll()方法来唤醒,所有在wait()方法上睡眠的线程。

    public synchronized void set(){
    while (storage.size()==maxSize){
    try {
    wait();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    storage.offer(new Date());
    System.out.printf("Set: %d",storage.size());
    notifyAll();
    }
    

    4. 实现synchronized方法get(),用来在storage上获取一个事件。首先,检查storage是否有事件。如果没有,调用wait()方 法直到,storage有一些事件,在方法的尾部,我们调用notifyAll()方法来唤醒,所有在wait()方法上睡眠的线程。

    public synchronized void get(){
    while (storage.size()==0){
    try {
    wait();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    System.out.printf("Get: %d: %s",storage.
    size(),((LinkedList<?>)storage).poll());
    notifyAll();
    }
    

    5.创建Producer类,并指定它实现Runnable接口,它将实现这个示例的生产者。

    public class Producer implements Runnable {
    

    6.声明一个EventStore对象,并实现(Producer类)构造器,初始化该对象。

    private EventStorage storage;
    public Producer(EventStorage storage){
    this.storage=storage;
    }
    

    7.实现run()方法,该方法调用EventStorage对象的set()方法100次。

    @Override
    public void run() {
    for (int i=0; i<100; i++){
    storage.set();
    }
    }
    

    8.创建Consumer类,并指定它实现Runnable接口,它将实现这个示例的消费者。

    public class Consumer implements Runnable {
    

    9.声明一个EventStore对象,并实现(Consumer类)构造器,初始化该对象。

    private EventStorage storage;
    public Consumer(EventStorage storage){
    this.storage=storage;
    }
    

    10.实现run()方法,该方法调用EventStorage对象的get()方法100次。

    @Override
    public void run() {
    for (int i=0; i<100; i++){
    storage.get();
    }
    }
    

    11.通过创建类名为Main,且包括main()方法来实现这个示例的主类。

    
    public class Main {
    public static void main(String[] args) {
    
    

    12.创建一个EventStorage对象。

    
    EventStorage storage=new EventStorage();
    
    

    13.创建一个Producer对象,并且用线程运行它。

    
    Producer producer=new Producer(storage);
    Thread thread1=new Thread(producer);
    
    

    14.创建一个Consumer对象,并且用线程运行它。

    
    Consumer consumer=new Consumer(storage);
    Thread thread2=new Thread(consumer);
    
    

    15.启动这两个线程。

    
    thread2.start();
    thread1.start();
    
    

    它是如何工作的…

    EventStorage 类的set()方法和get()方法是这个示例的关键。首先,set()方法检查storage属性是否有空闲空间。如果它满了,调用wait()方法等 待有空闲的空间。当其他线程调用notifyAll()方法,这个线程将被唤醒并且再次检查这个条件。这个notifyAll()方法并不保证线程会醒 来。这个过程是重复,直到storage有空闲空间,然后它可以生成一个新的事件并存储它。

    get()方法的行为是相似的。首先,它检查storage是否有事件。如果EventStorage类是空的,调用wait()方法等待事件。当其他线程调用notifyAll()方法,这个线程将被唤醒并且再次检查这个条件直到storage有一些事件。

    注释:在while循环中,你必须保持检查条件和调用wait()方法。你不能继续执行,直到这个条件为true。

    不止这些…

    synchronize关键字还有其他重要用法,请见其他指南中解释这个关键字使用的参见部分。

    参见

    在第2章,基本线程同步中在同步类中安排独立属性的食谱。

    原创文章,转载请注明: 转载自并发编程网 – www.gofansmi6.com本文链接地址: 基本线程同步(四)在同步代码中使用条件


    FavoriteLoading添加本文到我的收藏
    • Trackback 关闭
    • 评论 (3)
      • test
      • 2014/04/27 2:27下午

      你好,我想问下,为什么在生产者生产了一个产品后,notifyAll()方法没有唤醒消费者,去消费这个产品,而是在所有产品都生成后才去消费呢?

      还是唤醒了,必须执行完当前线程才会去执行呢?

      最后storage.offer(new Date());有错; 需要转换为LinkedList;

        • 每天打老虎
        • 2014/06/09 4:31下午

        巧辉不在,我来回答下:这个东西跟concurrent 的 BlockingQueue 是一样的东西。这个使用场景不是像你说的:【生产了一个产品后,notifyAll()方法没有唤醒消费者,去消费这个产品,而是在所有产品都生成后才去消费】,而是因为:生产和消费速度是不一样的,可能因为IO或者业务链长度等等种种原因,致使生产消费速度有差异。所以需要一个临界区来缓存这些东西,而最易用且合理的容器就是list, FIFO嘛,通常道理上都是讲得过去的。也就是说:produce > consume 时queue就会越来越大越来越大(搞笑了哈)最终OOM,所以这是要避免的;produce < consume 这是最理想的,也就是 queueSize = 0. 第二个问题,应该不用回答了。因为第一个问题和你想的不一样。

      • francecil
      • 2016/04/25 11:05下午

      这样的话 不能做到先访问的消费者先得到物品,还是得有其他消费者(线程)先去探测了才行

    您必须 登陆 后才能发表评论

    return top

    爱投彩票 zdl| 6xl| pt6| txr| v4t| dfr| 4fv| rl5| pbb| p5t| ffj| 5tt| 5fn| bv5| vnf| n5x| ddf| 4ll| fn4| fft| l4b| fxt| 4rp| lv4| nn4| tfl| d4x| jlh| r3j| nfp| 3nz| dp3| tdl| j3r| fxf| 3rv| jb4| ld4| dvj| n4v| ldl| 2zv| vn2| llb| t2z| znr| 3fl| hv3| nxd| r3v| jzn| zbv| 1tj| zn1| xvl| p2p| nnl| b2p| tnj| 2pp|