Pregunta

¿Qué debo usar para obtener la semántica equivalentes a AutoResetEvent en Java? (Ver esta pregunta para ManualResetEvent).

¿Fue útil?

Solución

@ respuesta de user249654 parecía prometedor. He añadido algunas pruebas de unidad para verificar que, de hecho, funciona como se esperaba.

También he añadido una sobrecarga de waitOne que toma un tiempo de espera.

El código es aquí en caso de cualquier otra persona considera útil:

Unidad de prueba

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 con sobrecarga que acepta un tiempo de espera

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;
    }
}

Otros consejos

class AutoResetEvent {

  private final Object monitor = new Object();
  private volatile boolean open = false;

  public AutoResetEvent(boolean open) {
    this.open = open;
  }

  public void waitOne() throws InterruptedException {
    synchronized (monitor) {
      while (open == false) { 
        monitor.wait();
      }
      open = false; // close for other
    }

  }

  public void set() {
    synchronized (monitor) {
      open = true;
      monitor.notify(); // open one 
    }
  }

  public void reset() {//close stop
    open = false;
  }
}

Yo era capaz de conseguir CyclicBarrier a trabajar para mis propósitos.

Este es el código C # yo estaba tratando de reproducir en Java (es sólo un programa de demostración que escribí para aislar el paradigma, ahora lo uso en programas de C # escribo para generar vídeo en tiempo real, para proporcionar un control preciso de la velocidad de cuadro):

using System;
using System.Timers;
using System.Threading;

namespace TimerTest
{
    class Program
    {
        static AutoResetEvent are = new AutoResetEvent(false);
        static void Main(string[] args)
        {
            System.Timers.Timer t = new System.Timers.Timer(1000);
            t.Elapsed += new ElapsedEventHandler(delegate { are.Set(); });
            t.Enabled = true;
            while (true)
            {
                are.WaitOne();
                Console.WriteLine("main");
            }
        }
    }
}

y aquí está el código Java que se me ocurrió hacer lo mismo (utilizando la clase CyclicBarrier como se sugiere en una respuesta anterior):

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CyclicBarrier;

public class TimerTest2 {
    static CyclicBarrier cb;

    static class MyTimerTask extends TimerTask {
        private CyclicBarrier cb;
        public MyTimerTask(CyclicBarrier c) { cb = c; }

        public void run() { 
            try { cb.await(); } 
            catch (Exception e) { } 
        }
    }

    public static void main(String[] args) {
        cb = new CyclicBarrier(2);
        Timer t = new Timer();
        t.schedule(new MyTimerTask(cb), 1000, 1000);

        while (true) {
            try { cb.await(); } 
            catch (Exception e) { }
            System.out.println("main");
        }
    }
}

Una extensión más a la solución de la respuesta aceptada en caso de que le gustaría saber si su espera terminó con tiempo de espera o con el conjunto de eventos (que es exactamente lo que hace .NET AutoResetEvent).

public boolean waitOne(long timeout) throws InterruptedException {
    synchronized (monitor) {
        try {
            long t = System.currentTimeMillis();
            while (!isOpen) {
                monitor.wait(timeout);
                // Check for timeout
                if (System.currentTimeMillis() - t >= timeout)
                    break;
            }

            return isOpen;
        }
        finally {
            isOpen = false;
        }
    }
}
import java.util.concurrent.TimeUnit;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.ReentrantLock;

public class AutoResetEvent {

  private volatile boolean _signaled;
  private ReentrantLock _lock;
  private Condition _condition;

  public AutoResetEvent(boolean initialState) {
    _signaled = initialState;
    _lock = new ReentrantLock();
    _condition = _lock.newCondition();
  }

  public void waitOne(long miliSecond) throws InterruptedException {
    _lock.lock();
    try {
      while (!_signaled)
        _condition.await(miliSecond, TimeUnit.MILLISECONDS);
      _signaled = false;
    } finally {
        _lock.unlock();
    }
  }

  public void waitOne() throws InterruptedException {
    _lock.lock();
    try {
      while (!_signaled)
        _condition.await();
      _signaled = false;
    } finally {
        _lock.unlock();
    }
  }

  public void set() {
    _lock.lock();
    try {
      _condition.signal();
      _signaled = true;
    } finally {
      _lock.unlock();
    }
  }

  public void reset() {
    _lock.lock();
    try {
      _signaled = false;
    } finally {
      _lock.unlock();
    }
  }
}

Creo que lo que estás buscando es o bien un CyclicBarrier o una CountDownLatch.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top