visual studio microsoft espaƱol descargar community java multithreading concurrency

java - microsoft - visual studio installer



Devuelve los valores de los subprocesos de Java (9)

Tengo un hilo de Java como el siguiente:

public class MyThread extends Thread { MyService service; String id; public MyThread(String id) { this.id = node; } public void run() { User user = service.getUser(id) } }

Tengo alrededor de 300 ids, y cada dos o tres segundos, enciendo hilos para hacer una llamada para cada uno de los id. P.ej.

for(String id: ids) { MyThread thread = new MyThread(id); thread.start(); }

Ahora, me gustaría recopilar los resultados de cada subprocesos y hacer una inserción por lotes en la base de datos, en lugar de hacer 300 inserciones de base de datos cada 2 segundos.

¿Alguna idea de cómo puedo lograr esto?


Almacene su resultado en su objeto. Cuando se complete, haga que caiga en una colección sincronizada (se le viene a la mente una cola sincronizada).

Cuando desee recopilar los resultados para enviarlos, tome todo de la cola y lea los resultados de los objetos. Incluso puede hacer que cada objeto sepa cómo "publicar" sus propios resultados en la base de datos, de esta forma se pueden enviar diferentes clases y todas se manejan con el mismo bucle pequeño y elegante.

Hay muchas herramientas en el JDK para ayudar con esto, pero es realmente fácil una vez que empiezas a pensar en tu hilo como un verdadero objeto y no solo en un montón de basura en torno a un método de "ejecución". Una vez que comienzas a pensar en los objetos, la programación se vuelve mucho más simple y satisfactoria.


El enfoque canónico consiste en utilizar un servicio Callable y un servicio ExecutorService . submit un Callable a un ExecutorService devuelve un futuro (tipo seguro) desde el que puede get el resultado.

class TaskAsCallable implements Callable<Result> { @Override public Result call() { return a new Result() // this is where the work is done. } } ExecutorService executor = Executors.newFixedThreadPool(300); Future<Result> task = executor.submit(new TaskAsCallable()); Result result = task.get(); // this blocks until result is ready

En su caso, es probable que desee utilizar invokeAll que devuelve una List de Futures , o cree esa lista usted mismo cuando agregue tareas al ejecutor. Para recopilar resultados, simplemente llame a cada uno.


El enfoque más simple es pasar un objeto a cada hilo (un objeto por hilo) que contendrá el resultado más tarde. El hilo principal debe mantener una referencia a cada objeto de resultado. Cuando se unen todos los hilos, puede usar los resultados.


Necesita almacenar el resultado en algo así como singleton. Esto tiene que estar correctamente sincronizado.

EDITAR : Sé que este no es el mejor consejo, ya que no es una buena idea manejar Threads procesar. Pero dada la pregunta, esto funcionará, ¿no? No puedo ser votada, pero ¿por qué votar abajo?


Podrías hacer una clase que amplía Observable. Entonces su hilo puede llamar a un método en la clase Observable que notificaría a cualquier clase que se registró en ese observador llamando a Observable.notifyObservers (Object).

La clase de observación implementará Observer y se registrará con el Observable. Luego implementaría un método de actualización (observable, objeto) que se llama cuando se llama a Observerable.notifyObservers (Object).


Puede crear una cola o lista que pase a los hilos que cree, los hilos añaden su resultado a la lista que se vacía por un consumidor que realiza la inserción del lote.


public class TopClass { List<User> users = new ArrayList<User>(); void addUser(User user) { synchronized(users) { users.add(user); } } void store() throws SQLException { //storing code goes here } class MyThread extends Thread { MyService service; String id; public MyThread(String id) { this.id = node; } public void run() { User user = service.getUser(id) addUser(user); } } }


En Java8 hay una mejor manera de hacerlo usando CompletableFuture . Digamos que tenemos una clase que obtiene el ID de la base de datos, por simplicidad solo podemos devolver un número como se muestra a continuación,

static class GenerateNumber implements Supplier<Integer>{ private final int number; GenerateNumber(int number){ this.number = number; } @Override public Integer get() { try { TimeUnit.SECONDS.sleep(1); }catch (InterruptedException e){ e.printStackTrace(); } return this.number; } }

Ahora podemos agregar el resultado a una colección concurrente una vez que los resultados de cada futuro estén listos.

Collection<Integer> results = new ConcurrentLinkedQueue<>(); int tasks = 10; CompletableFuture<?>[] allFutures = new CompletableFuture[tasks]; for (int i = 0; i < tasks; i++) { int temp = i; CompletableFuture<Integer> future = CompletableFuture.supplyAsync(()-> new GenerateNumber(temp).get(), executor); allFutures[i] = future.thenAccept(results::add); }

Ahora podemos agregar una devolución de llamada cuando todos los futuros estén listos,

CompletableFuture.allOf(allFutures).thenAccept(c->{ System.out.println(results); // do something with result });


Si desea recopilar todos los resultados antes de realizar la actualización de la base de datos, puede usar el método invokeAll . Esto se ocupa de la contabilidad que se requeriría si envía tareas de una en una, como sugiere daveb .

private static final ExecutorService workers = Executors.newCachedThreadPool(); ... Collection<Callable<User>> tasks = new ArrayList<Callable<User>>(); for (final String id : ids) { tasks.add(new Callable<User>() { public User call() throws Exception { return svc.getUser(id); } }); } /* invokeAll blocks until all service requests complete, * or a max of 10 seconds. */ List<Future<User>> results = workers.invokeAll(tasks, 10, TimeUnit.SECONDS); for (Future<User> f : results) { User user = f.get(); /* Add user to batch update. */ ... } /* Commit batch. */ ...