001    
002    /*
003     * Copyright (C) 2011 Archie L. Cobbs. All rights reserved.
004     *
005     * $Id: ThreadParkingLot.java 104 2011-05-12 18:03:42Z archie.cobbs $
006     */
007    
008    package org.dellroad.stuff.java;
009    
010    import java.util.HashSet;
011    
012    /**
013     * A place for threads to be parked and unparked.
014     *
015     * @since 1.0.102
016     */
017    public class ThreadParkingLot {
018    
019        private final HashSet<Thread> parkedThreads = new HashSet<Thread>();
020    
021        /**
022         * Park the current thread on this instance. Execution will halt until {@link #unpark unpark()} is invoked
023         * by some other thread with the current thread as the parameter, the given non-zero timeout expires, or
024         * the current thread is interrupted.
025         *
026         * @param timeout maximum time to stay parked, or zero to park indefinitely
027         * @return {@code true} if the thread was unparked by another thread, {@code false} if the timeout expired
028         * @throws IllegalArgumentException if {@code timeout} is negative
029         * @throws InterruptedException if the current thread is interrupted
030         */
031        public synchronized boolean park(long timeout) throws InterruptedException {
032            final Thread thread = Thread.currentThread();
033            this.parkedThreads.add(thread);
034            try {
035                return TimedWait.wait(this, timeout, new Predicate() {
036                    @Override
037                    public boolean test() {
038                        return !ThreadParkingLot.this.parkedThreads.contains(thread);
039                    }
040                });
041            } finally {
042                this.parkedThreads.remove(thread);
043            }
044        }
045    
046        /**
047         * Unpark a thread.
048         *
049         * @param thread the thread to unpark
050         * @return {@code true} if {@code thread} was successfully unparked, {@code false} if {@code thread}
051         *  is not parked on this instance
052         */
053        public synchronized boolean unpark(Thread thread) {
054            boolean wasParked = this.parkedThreads.remove(thread);
055            if (wasParked)
056                this.notifyAll();
057            return wasParked;
058        }
059    
060        /**
061         * Determine if the given thread is currently parked on this instance.
062         *
063         * @param thread the thread in question
064         * @return {@code true} if {@code thread} is currently parked on this instance, {@code false} otherwise
065         */
066        public synchronized boolean isParked(Thread thread) {
067            return this.parkedThreads.contains(thread);
068        }
069    }
070