Search code examples
javaeventsconcurrencysynchronizationautoresetevent

Java's equivalent to .Net's AutoResetEvent?


What should I use to get semantics equivalent to AutoResetEvent in Java? (See this question for ManualResetEvent).


Solution

  • @user249654's answer looked promising. I added some unit tests to verify it, and indeed it works as expected.

    I also added an overload of waitOne that takes a timeout.

    The code is here in case anyone else finds it useful:

    Unit Test

    import org.junit.Assert;
    import org.junit.Test;
    
    import static java.lang.System.currentTimeMillis;
    
    /**
     * @author Drew Noakes http://drewnoakes.com
     */
    public class AutoResetEventTest
    {
        @Test
        public void synchronisesProperly() throws InterruptedException
        {
            final AutoResetEvent event1 = new AutoResetEvent(false);
            final AutoResetEvent event2 = new AutoResetEvent(false);
            final int loopCount = 10;
            final int sleepMillis = 50;
    
            Thread thread1 = new Thread(new Runnable()
            {
                @Override
                public void run()
                {
                    try {
                        for (int i = 0; i < loopCount; i++)
                        {
                            long t = currentTimeMillis();
                            event1.waitOne();
                            Assert.assertTrue("Time to wait should be within 5ms of sleep time",
                                    Math.abs(currentTimeMillis() - t - sleepMillis) < 5);
                            Thread.sleep(sleepMillis);
                            t = currentTimeMillis();
                            event2.set();
                            Assert.assertTrue("Time to set should be within 1ms", currentTimeMillis() - t <= 1);
                        }
                    } catch (InterruptedException e) {
                        Assert.fail();
                    }
                }
            });
    
            Thread thread2 = new Thread(new Runnable()
            {
                @Override
                public void run()
                {
                    try {
                        for (int i = 0; i < loopCount; i++)
                        {
                            Thread.sleep(sleepMillis);
                            long t = currentTimeMillis();
                            event1.set();
                            Assert.assertTrue("Time to set should be within 1ms", currentTimeMillis() - t <= 1);
                            t = currentTimeMillis();
                            event2.waitOne();
                            Assert.assertTrue("Time to wait should be within 5ms of sleep time",
                                    Math.abs(currentTimeMillis() - t - sleepMillis) < 5);
                        }
                    } catch (InterruptedException e) {
                        Assert.fail();
                    }
                }
            });
    
            long t = currentTimeMillis();
    
            thread1.start();
            thread2.start();
    
            int maxTimeMillis = loopCount * sleepMillis * 2 * 2;
    
            thread1.join(maxTimeMillis);
            thread2.join(maxTimeMillis);
    
            Assert.assertTrue("Thread should not be blocked.", currentTimeMillis() - t < maxTimeMillis);
        }
    
        @Test
        public void timeout() throws InterruptedException
        {
            AutoResetEvent event = new AutoResetEvent(false);
    
            int timeoutMillis = 100;
            long t = currentTimeMillis();
            event.waitOne(timeoutMillis);
            long took = currentTimeMillis() - t;
            Assert.assertTrue("Timeout should have occurred, taking within 5ms of the timeout period, but took " + took,
                    Math.abs(took - timeoutMillis) < 5);
        }
    
        @Test
        public void noBlockIfInitiallyOpen() throws InterruptedException
        {
            AutoResetEvent event = new AutoResetEvent(true);
    
            long t = currentTimeMillis();
            event.waitOne(200);
            Assert.assertTrue("Should not have taken very long to wait when already open",
                    Math.abs(currentTimeMillis() - t) < 5);
        }
    }
    

    AutoResetEvent with overload that accepts a timeout

    public class AutoResetEvent
    {
        private final Object _monitor = new Object();
        private volatile boolean _isOpen = false;
    
        public AutoResetEvent(boolean open)
        {
            _isOpen = open;
        }
    
        public void waitOne() throws InterruptedException
        {
            synchronized (_monitor) {
                while (!_isOpen) {
                    _monitor.wait();
                }
                _isOpen = false;
            }
        }
    
        public void waitOne(long timeout) throws InterruptedException
        {
            synchronized (_monitor) {
                long t = System.currentTimeMillis();
                while (!_isOpen) {
                    _monitor.wait(timeout);
                    // Check for timeout
                    if (System.currentTimeMillis() - t >= timeout)
                        break;
                }
                _isOpen = false;
            }
        }
    
        public void set()
        {
            synchronized (_monitor) {
                _isOpen = true;
                _monitor.notify();
            }
        }
    
        public void reset()
        {
            _isOpen = false;
        }
    }