/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat, Inc. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.hibernate.search.batchindexing.impl; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.atomic.AtomicInteger; /** * Implements a blocking queue capable of storing * a "poison" token to signal consumer threads * that the task is finished. * * @author Sanne Grinovero */ public class ProducerConsumerQueue<T> { private static final int DEFAULT_BUFF_LENGHT = 1000; private static final Object exitToken = new Object(); //doesn't use generics here as exitToken needs to be put in the queue too: @SuppressWarnings("unchecked") private final BlockingQueue queue; private final AtomicInteger producersToWaitFor; /** * @param producersToWaitFor The number of producer threads. */ public ProducerConsumerQueue( int producersToWaitFor ) { this( DEFAULT_BUFF_LENGHT, producersToWaitFor ); } @SuppressWarnings("unchecked") public ProducerConsumerQueue( int queueLenght, int producersToWaitFor ) { queue = new ArrayBlockingQueue( queueLenght ); this.producersToWaitFor = new AtomicInteger( producersToWaitFor ); } /** * Blocks until an object is available; when null * is returned the client thread should quit. * @return the next object in the queue, or null to exit * @throws InterruptedException */ @SuppressWarnings("unchecked") public T take() throws InterruptedException { Object obj = queue.take(); if ( obj == exitToken ) { //restore exit signal for other threads queue.put( exitToken ); return null; } else { return (T)obj; } } /** * Adds a new object to the queue, blocking if no space is * available. * @param obj * @throws InterruptedException */ @SuppressWarnings("unchecked") public void put(T obj) throws InterruptedException { queue.put( obj ); } /** * Each producer thread should call producerStopping() when it has * finished. After doing it can safely terminate. * After all producer threads have called producerStopping() * a token will be inserted in the blocking queue to eventually * awake sleeping consumers and have them quit, after the * queue has been processed. */ @SuppressWarnings("unchecked") public void producerStopping() { int activeProducers = producersToWaitFor.decrementAndGet(); //last producer must close consumers if ( activeProducers == 0 ) { try { queue.put( exitToken );//awake all waiting threads to let them quit. } catch (InterruptedException e) { //just quit, consumers will be interrupted anyway if it's a shutdown. Thread.currentThread().interrupt(); } } } }