package com.limegroup.gnutella.util; import java.util.List; import java.util.Vector; /** * A queue of items to be processed. * * The queue processes the items in a seperate thread, allowing * the thread to be released when all items are processed. */ public class ProcessingQueue { /** * The list of items to be processed. */ private final List QUEUE = new Vector(); /** * The name of the thread to be created. */ private final String NAME; /** * Whether or not the constructed thread should be a managed thread or * not. */ private final boolean MANAGED; /** * The thread doing the processing. */ private Thread _runner = null; /** * Constructs a new ProcessingQueue using a ManagedThread. */ public ProcessingQueue(String name) { this(name, true); } /** * Constructs a new ProcessingQueue of the given name. If managed * is true, uses a ManagedThread for processing. Otherwise uses * a normal thread. */ public ProcessingQueue(String name, boolean managed) { NAME = name; MANAGED = managed; } /** * Add the specified runnable to be processed. */ public synchronized void add(Runnable r) { QUEUE.add(r); notify(); if(_runner == null) startRunner(); } /** * Starts a new runner. */ private synchronized void startRunner() { if(MANAGED) _runner = new ManagedThread(new Processor(), NAME); else _runner = new Thread(new Processor(), NAME); _runner.setDaemon(true); _runner.start(); } /** * The runnable that processes the queue. */ private class Processor implements Runnable { public void run() { try { while(true) { while(QUEUE.size() > 0) { Runnable next = (Runnable)QUEUE.remove(0); next.run(); } // Ideally this would be in a finally clause -- but if it // is then we can potentially ignore exceptions that were // thrown. synchronized(ProcessingQueue.this) { // If something was added before we grabbed the lock, // process those items immediately instead of waiting if(!QUEUE.isEmpty()) continue; // Wait a little bit to see if something new is going // to come in, so we don't needlessly kill/recreate // threads. try { ProcessingQueue.this.wait(5 * 1000); } catch(InterruptedException ignored) {} // If something was added and notified us, process it // instead of exiting. if(!QUEUE.isEmpty()) continue; // Otherwise, exit else break; } } } finally { // We must restart a new runner if something was added. // It's highly unlikely that something was added between // the try of one synchronized block & the finally of another, // but it technically is possible. // We cannot loop here because we'd lose any exceptions // that may have been thrown. synchronized(ProcessingQueue.this) { if(!QUEUE.isEmpty()) startRunner(); else _runner = null; } } } } }