// Copyright (c) 2001 Dustin Sallings <dustin@spy.net> package net.spy.concurrent; import java.util.Random; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; import junit.framework.TestCase; import net.spy.SpyObject; /** * Test the ThreadPool. */ public class ThreadPoolTest extends TestCase { // Thread IDs (inner classes can't have static fields) static int ids=0; /** * Test the constructors. */ public void testConstructors() { try { ThreadPool tp=new ThreadPool("Test1", 1, 1, Thread.MIN_PRIORITY-1); fail("Was able to set a priority of " + (Thread.MIN_PRIORITY-1)); tp.shutdown(); } catch(IllegalArgumentException e) { // pass } try { ThreadPool tp=new ThreadPool("Test2", 1, 1, Thread.MAX_PRIORITY+1); fail("Was able to set a priority of " + (Thread.MAX_PRIORITY+1)); tp.shutdown(); } catch(IllegalArgumentException e) { // pass } ThreadPool tp=new ThreadPool("Test3", 25); tp.start(); tp.shutdown(); } /** * Test the basics of the thread pool. */ public void testBasicThreadPool() throws Exception { ThreadPool tp=new ThreadPool("TestThreadPool", 15, 20, Thread.NORM_PRIORITY, new LinkedBlockingQueue<Runnable>(50)); // Test string before start String.valueOf(tp); assertEquals("Incorrect thread count", 0, tp.getLargestPoolSize()); // Starting the pool tp.start(); // Make sure the priority is where we expect it. assertEquals(Thread.NORM_PRIORITY, tp.getPriority()); // Make sure toString() doesn't error String.valueOf(tp); // Verify the start threads started. assertEquals("Start threads incorrect", 15, tp.getPoolSize()); // Add some tasks for(int i=0; i<70; i++) { tp.addTask(new TestRunnable()); // Give it a little time so the threads can ramp up. Thread.yield(); } Thread.sleep(100); // Verify the thread count has risen to accommodate. assertEquals("Incorrect thread count", 20, tp.getLargestPoolSize()); assertEquals("Incorrect thread count", 20, tp.getPoolSize()); // Wait long enough for the jobs to all be accepted (at most, they // should be 5 seconds each, 20 at a time, for 70 tasks, giving us // seven seconds (I think)) Thread.sleep(10000); // Verify they all got accepted assertTrue("Not all jobs accepted", tp.getQueue().size() == 0); // Add another one with a timeout (this will also trigger the run loop // which should notice there are too many threads). TestRunnable t=new TestRunnable(); boolean started=tp.addTask(t, 500); assertFalse("New task wasn't accepted", started); // Give it a bit to start Thread.sleep(100); assertTrue("New task wasn't started", t.wasRun()); // Wait for the monitor to notice and do something about it. Thread.sleep(1500); // Verify the thread count has risen to accommodate. assertTrue("Thread count not decreasing", tp.getPoolSize() < 20); // OK, now let's make sure the threads are busy for(int i=0; i<50; i++) { tp.addTask(new TestRunnable()); } // Now that that's done, we want to add a thread with a timeout and // verify it *doesn't* make it. t=new TestRunnable(); started=tp.addTask(t, 1000); assertTrue("New task shouldn't have been accepted", (!started)); // Wait for the queue to die down to zero tp.waitForTaskCount(0); // Sleep just a second longer, so things can get initialized Thread.sleep(1000); // Now, verify the task did not start assertTrue("New task shouldn't have started", (!t.wasRun())); // Happier shutdown tp.shutdown(); } public void testShutdown() throws Exception { ThreadPool tp=new ThreadPool("TestThreadPool"); tp.addTask(new TestRunnable()); // Happier shutdown tp.waitForCompletion(); } public void testOverflow() throws Exception { ThreadPool tp=new ThreadPool("test pool", 1, 10, Thread.NORM_PRIORITY, 2); try { // Add more than it can take. for(int i=0; i<10; i++) { tp.addTask(new TestRunnable()); } } catch(RejectedExecutionException e) { assertNull(e.getMessage()); } finally { tp.shutdown(); } } private static class TestRunnable extends SpyObject implements Runnable { private int id=0; boolean wasRun=false; public TestRunnable() { super(); synchronized(TestRunnable.class) { id=ids++; } } public boolean wasRun() { return(wasRun); } public void run() { // Make sure this wasn't run more than once. synchronized(this) { if(wasRun) { throw new IllegalStateException( "This task has already been run."); } wasRun=true; } try { Random rand=new Random(); // Get things that sleep between 1 and 5 seconds. long l=1000 + Math.abs(rand.nextLong()%4000); getLogger().info(id + ": sleeping for " + l); Thread.sleep(l); getLogger().info(id + ": done!"); } catch(InterruptedException e) { throw new RuntimeException("Interrupted"); } } } }