Pregunta

estoy usando un ThreadPoolExecutor con un tamaño de grupo de subprocesos de uno a ejecutar de forma secuencial swing de los trabajadores.Tengo un caso especial en el que un evento llega que crea un swing trabajador que hace la comunicación cliente-servidor y después de que las actualizaciones de la interfaz de usuario (en el hecho() método).

Esto funciona bien cuando el usuario incendios (hace clic en un elemento) algunos de los eventos, pero no si no se producen muchos de ellos.Pero esto sucede así que tengo que cancelar la ejecución de todos los programados y los trabajadores.El problema es que la cola que es la copia de la ThreadPoolExecutor no es consciente de la SwingWorker proceso de cancelación (al menos parece que eso).De manera programada trabajador obtener cancelado, pero ya se ejecutan los trabajadores de no conseguir.

Así que he añadido una cola de tipo concurrente <T extends SwingWorker> que contiene una referencia de todos los trabajadores, siempre que no sean canceladas y cuando un nuevo evento llega llamadas .cancelar(true) en todos los SwingWorkers en la cola y presenta la nueva SwingWorker para la ThreadPoolExecutor.

Resumen:SwingWorkers son creadas y ejecutadas en un ThreadPoolExecutor con un solo Hilo.Sólo el trabajador que fue presentado el pasado debe estar en ejecución.

¿Existen alternativas para resolver este problema, o es "aceptar" para hacer esto de esta manera?

Sólo por curiosidad...

¿Fue útil?

Solución

Una forma de crear un ThreadPoolExecutor de subproceso único que solo ejecuta el último Runnable entrante es subclasificar una clase de cola adecuada y anular todos los métodos de adición para borrar la cola antes de agregar el nuevo ejecutable. Luego configure esa cola como la cola de trabajo de ThreadPoolExecutor.

Otros consejos

¿Por qué necesita un ThreadPoolExecutor para hacer este tipo de trabajo?

¿Cuántas fuentes de diferentes SwingWorkers tienes? Porque si la fuente es solo una, deberías usar un enfoque diferente.

Por ejemplo, puede definir una clase que maneje un tipo de subproceso de trabajo y esté vinculada a un solo tipo de elemento en el que el usuario puede disparar acciones y cuidar dentro de esa clase el hecho de que una sola instancia del subproceso debería estar ejecutándose (por ejemplo, usando una instancia singleton que se borra al finalizar la tarea)

En lugar de usar un SwingWorker, ¿no podría usar un ThreadPoolExecutor para realizar la comunicación cliente-servidor y luego llamar a SwingUtilities.invokeLater para actualizar la interfaz de usuario con el resultado? Esto me parece un poco más limpio y garantizaría que los eventos y las actualizaciones de la interfaz de usuario aún se procesen en orden.

Al enviar tareas a su ejecutor, puede mantener una referencia a sus instancias futuras para que pueda cancelar la tarea si es necesario.

Déjame ver si entiendo el problema correctamente.Tiene una cola FIFO de tareas, sólo la más antigua de la que se está ejecutando.Cada tarea se necesita actualizar la interfaz de usuario cuando se hace.Pero si un determinado usuario evento se presenta en, todas las tareas deben ser cancelados -- que es, la ejecución de las tareas necesitan ser cancelado, y que aún no se ejecuta tareas deben ser eliminados de la cola.Es ese derecho?

Suponiendo que sea, yo no lo uso SwingWorker ya que sólo necesita un subproceso de trabajo, no uno por cada tarea. FutureTask debería ser suficiente (suponiendo que reemplazar done() para hacer las llamadas necesarias para SwingUtilities.invokeLater() y hacer la interfaz de usuario de actualización).

Si se cancela una FutureTask, entonces , aun si su run() método se llama, no hacer nada.Así que usted puede enviar FutureTasks de forma segura a un ExecutorService sabiendo que la cancelación funcionará incluso si el ejecutor intenta ejecutar ellos.

Sospecho que una buena cantidad suficiente de solución sería mantener una lista de todos los FutureTasks que deben ser cancelados, y cancelar todos ellos cuando el suceso de usuario que viene.El ExecutorService todavía intentará ejecutar, pero, básicamente, va a ser un no-op.Usted necesita para asegurarse de que las tareas completadas se eliminan de la lista, y usted necesita para asegurarse de que la lista se actualiza y se utiliza en un hilo de manera segura (probablemente desde el mismo hilo que pone tareas en el ExecutorService), pero que no debería ser demasiado difícil.

Me reventaron el código de abajo en apenas una hora y yo no apostaría por ello de ser correcta, pero usted consigue la idea.:)

/** Untested code! Use at own risk. */
public class SwingTaskExecutor {

    // ////////////////////////////////////////////////////////////
    // Fields

    private final ExecutorService execSvc = Executors.newFixedThreadPool(1);

    private final Lock listLock = new ReentrantLock();
    private final List<ManagedSwingTask<?>> activeTasks = 
            new ArrayList<ManagedSwingTask<?>>();

    // ////////////////////////////////////////////////////////////
    // Public methods

    public <T> Future<T> submit(SwingTask<T> task) {
        ManagedSwingTask<T> managedTask = new ManagedSwingTask<T>(task);
        addToActiveTasks(managedTask);
        execSvc.submit(managedTask);
        return managedTask;
    }

    public void cancelAllTasks() {
        listLock.lock();
        try {
            for (ManagedSwingTask<?> t: activeTasks) {
                t.cancel(true);
            }
            activeTasks.clear();
        } finally {
            listLock.unlock();
        }
    }

    // ////////////////////////////////////////////////////////////
    // Private methods

    private <T> void addToActiveTasks(ManagedSwingTask<T> managedTask) {
        listLock.lock();
        try {
            activeTasks.add(managedTask);
        } finally {
            listLock.unlock();
        }
    }

    // ////////////////////////////////////////////////////////////
    // Helper classes

    private class ManagedSwingTask<T> extends FutureTask<T> {

        private final SwingTask<T> task;

        ManagedSwingTask(SwingTask<T> task) {
            super(task);
            this.task = task;
        }

        @Override
        public void cancel(boolean mayInterruptIfRunning) {
            try {
                task.cancel();
            } finally {
                super.cancel(mayInterruptIfRunning);
            }
        }

        @Override
        protected void done() {
            removeFromActiveTasks();
            updateUIIfDone();
        }

        private void removeFromActiveTasks() {
            listLock.lock();
            try {
                activeTasks.remove(this);
            } finally {
                listLock.unlock();
            }
        }

        private void updateUIIfDone() {
            if (isDone()) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        task.updateUI();
                    }
                });
            }
        }
    }

    public static interface SwingTask<T> extends Callable<T> {

        /** Called from the EDT if task completes successfully */
        void updateUI();

        /** Hook in case there's task-specific cancellation to be done*/
        void cancel();
    }
}

Algo así como que, de todos modos.

Si quieres estar doblemente seguro, usted podría, a continuación, apague y reemplazar el ExecutorService, pero que probablemente no es necesario.

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