[Resolvido] Fork/Join no Java 6

5 respostas
kanegae

Estou desenvolvendo uma aplicação web que deve processar uma lista de itens, onde para cada item é necessário invocar uma URL que retorna um JSON, interpretar esse JSON e armazenar o resultado em um atributo do item.

Até o momento, devido a pequena quantidade de itens na lista a ser processada, era possível realizar o processo proceduralmente:

public ArrayList<E> processar(ArrayList<E> arrayList) { // TODO processar cada item do ArrayList return arrayList; }Devido ao crescimento exponencial da quantidade de itens na lista a ser processada, realizar o processo proceduralmente se tornou inviável. Logo, pensei em dividir a lista em partes menores, processar paralelamente as partes e unir o resultado:

public ArrayList<E> processar(ArrayList<E> arrayList) { // TODO dividir ArrayList, processar paralelamente as partes e unir o resultado de todas as partes return arrayList; }Entretanto, ainda não consegui imaginar como eu poderia implementar essa técnica e devido a isso, gostaria de receber sugestões de como implementá-la e/ou até mesmo sugestões de outras técnicas que possam ser uteis para resolver o meu problema.

5 Respostas

sergiotaborda

kanegae:
Estou desenvolvendo uma aplicação web que deve processar uma lista de itens, onde para cada item é necessário invocar uma URL que retorna um JSON, interpretar esse JSON e armazenar o resultado em um atributo do item.

Até o momento, devido a pequena quantidade de itens na lista a ser processada, era possível realizar o processo proceduralmente:

public ArrayList<E> processar(ArrayList<E> arrayList) { // TODO processar cada item do ArrayList return arrayList; }Devido ao crescimento exponencial da quantidade de itens na lista a ser processada, realizar o processo proceduralmente se tornou inviável. Logo, pensei em dividir a lista em partes menores, processar paralelamente as partes e unir o resultado:

public ArrayList<E> processar(ArrayList<E> arrayList) { // TODO dividir ArrayList, processar paralelamente as partes e unir o resultado de todas as partes return arrayList; }Entretanto, ainda não consegui imaginar como eu poderia implementar essa técnica e devido a isso, gostaria de receber sugestões de como implementá-la e/ou até mesmo sugestões de outras técnicas que possam ser uteis para resolver o meu problema.

não entendi a sua pergunta. Vc já deu a resposta no titulo. usar o fork/join no java 6 ( que está disponível como uma lib. no java 7 vem dentro do sdk)

A ideia é partir o array (não sei porque não usa List, mas tudo bem) em partes (fork) e mandar para o framework. A cada item do array o framework vai fazer uma ação. Ele vai se encarregar de distribuir esse trabalho entre várias threads. no fim, vc precisa agregar (join) o resultado no array de retorno. Basicamente é isto. Mas o algoritmo especifico depende do seu algoritmo.

mais detalhes em http://docs.oracle.com/javase/tutorial/essential/concurrency/forkjoin.html e http://www.javacodegeeks.com/2011/02/java-forkjoin-parallel-programming.html

kanegae

Obrigado sergiotaborda!
Eu desconhecia a possibilidade de usar a API Fork/Join do Java 7 no Java 6.
Mais detalhes em: http://www.vogella.com/articles/JavaConcurrency/article.html#forkjoin

gomesrod

Só lembrando de uma coisa, em um container Java EE não é recomendado usar threads que não sejam gerenciadas pelo servidor. Como eles dizem “the results are unpredictable” :slight_smile:

Teste bem a solução no servidor que estiver usando, e deixe isso bem documentado pois se um dia as threads tiverem algum comportamento estranho vocês se lembrarão por onde começar a investigar.

bombbr

Uma forma simples de fazer isto é criar ExecutorService para executar suas Threads, depois basta aguardar que as Threads finalizem e “somar” os resultados.

Abaixo um exemplo básico, para você ter uma idéia. A partir deste exemplo você pode sofisticar a solução, de forma deixar elástica a quantidade de partes e Threads simultâneas.

ExecutorService es = Executors.newFixedThreadPool(4);
//ou 
//ExecutorService es = Executors.newCachedThreadPool();

List arrayGrande = new ArrayList();
// separa o processamento em 4 partes
Future<Resultado> parte1 = es.submit(new Processador(arrayGrande, 1, 100000));
Future<Resultado> parte2 = es.submit(new Processador(arrayGrande, 100001, 200000));
Future<Resultado> parte3 = es.submit(new Processador(arrayGrande, 200001, 300000));
Future<Resultado> parte4 = es.submit(new Processador(arrayGrande, 300001, 400000));

while(!parte1.isDone() && !parte2.isDone() && !parte3.isDone() && !parte4.isDone()) {
	System.out.println("Aguardando....");
	Thread.sleep(1000);
}
//Aqui já terminou o processamento 
Resultado result1 = parte1.get();
Resultado result2 = parte2.get();
Resultado result3 = parte3.get();
Resultado result4 = parte4.get();

//Aqui você "junta" o resultado, ou passa para o próximo passo de processamento


es.shutdown();


public class Processador implements Callable<Resultado> {
//TODO:
}
sergiotaborda
bombbr:
Uma forma simples de fazer isto é criar ExecutorService para executar suas Threads, depois basta aguardar que as Threads finalizem e "somar" os resultados.

Abaixo um exemplo básico, para você ter uma idéia. A partir deste exemplo você pode sofisticar a solução, de forma deixar elástica a quantidade de partes e Threads simultâneas.

// separa o processamento em 4 partes
Future<Resultado> parte1 = es.submit(new Processador(arrayGrande, 1, 100000));
Future<Resultado> parte2 = es.submit(new Processador(arrayGrande, 100001, 200000));
Future<Resultado> parte3 = es.submit(new Processador(arrayGrande, 200001, 300000));
Future<Resultado> parte4 = es.submit(new Processador(arrayGrande, 300001, 400000));

/*
while(!parte1.isDone() && !parte2.isDone() && !parte3.isDone() && !parte4.isDone()) {
	System.out.println("Aguardando....");
	Thread.sleep(1000);
}
*/

//Aqui não importa se já terminou.
Resultado result1 = parte1.get();
Resultado result2 = parte2.get();
Resultado result3 = parte3.get();
Resultado result4 = parte4.get();
//Aqui já terminou o processamento

É, dá para fazer assim, desde que as threads sejam gerenciadas como o gomesrod falou.
Só que não precisa do while. o método get vai ficar trancado até que o futuro retorne. Portando a sequência de gets vai pegando um a um conforme o anterior é liberado.

Criado 11 de dezembro de 2012
Ultima resposta 11 de dez. de 2012
Respostas 5
Participantes 4