Como criar vários threads para cada item de solicitação

9

Estou tentando processar o código abaixo usando multithreading no nível do pedido.

List<String> orders = Arrays.asList("order1", "order2", 
                   "order3", "order4", "order1");

Execução sequencial atual:

orders.stream().forEach(order -> {
    rules.forEach(rule -> {
        finalList.add(beanMapper.getBean(rule)
                .applyRule(createTemplate.apply(getMetaData.apply(rule), command),
                           order));
    });
});

Eu tentei usar:

orders.parallelStream().forEach(order -> {}} // code snippet.

Mas está mudando a ordem rules.forEach (rule -> {}} .

Por exemplo:
Entrada:

 List<String> orders = Arrays.asList("order1", "order2", 
                         "order3", "order4", "order1");
 List<String> rules = Arrays.asList("rule1", "rule2", "rule3");

Saída esperada:

order1 with rule1, rule2, rule3
order2 with rule1, rule2, rule3

Saída real com parallelStream():

order1 with rule3, rule1, rule2
order1 with rule2, rule1, rule3

Não estou preocupado com a ordem das ordens , mas estou preocupado com a ordem das regras . Os pedidos podem processar em qualquer ordem, mas as regras devem ser executadas na mesma ordem para cada pedido.

Por favor ajude.

Mayank Bisht
fonte

Respostas:

4

Você pode usar :

orders.stream().parallel().forEachOrdered(// Your rules logic goes here. )

O ForEachOrdered garante a manutenção da ordem do fluxo.

Então, para sua referência:

orders.stream().parallel().forEachOrdered( order -> {

            rules.stream().parallel().forEachOrdered ( rule -> {

                 System.out.println( " Order : " + order + " rule :" + rule);
            });

        });

Nota: Embora possamos fazer isso, o desempenho deve ser observado de perto, porque o paralelismo e a ordem não se casam muito bem!

Resultado

 Order : order1 rule :rule1
 Order : order1 rule :rule2
 Order : order1 rule :rule3
 Order : order2 rule :rule1
 Order : order2 rule :rule2
 Order : order2 rule :rule3
 Order : order3 rule :rule1
 Order : order3 rule :rule2
 Order : order3 rule :rule3
 Order : order4 rule :rule1
 Order : order4 rule :rule2
 Order : order4 rule :rule3
 Order : order1 rule :rule1
 Order : order1 rule :rule2
 Order : order1 rule :rule3
Pramod S. Nikam
fonte
Obrigado pela resposta. O forEachOrdered garante a ordem do fluxo, mas também diminui o desempenho. Eu tentei e o aplicativo está demorando semelhante ao processamento seqüencial. stream (). parallel & forEachOrdered não se complementam.
23419 Mayank Bisht
Sim, concordo que precisamos fazer uma análise completa da latência antes de fazer isso.
Pramod S. Nikam
Sim, estou obtendo o mesmo desempenho usando isso, não há melhorias.
mayank bisht
11
Seguindo atentamente este tópico para obter uma solução melhor para fazer isso.
Pramod S. Nikam
Posso obter processamento paralelo usando o ExecutorService?
mayank bisht
1

Você adiciona elementos ao finalListde diferentes segmentos ao mesmo tempo. Isso está causando resultados conflitantes da aplicação de regras a diferentes pedidos (as regras não estão sendo agrupadas por seus pedidos).

Você pode corrigi-lo criando uma lista temporária para cada uma ordere mesclando sincronamente todas as listas temporárias em a finalList.

Aqui está como você pode fazer isso usando a API de fluxo (Java 9+):

List<AppliedRule> finalList = orders.parallelStream().map(order ->
        rules.stream().map(rule -> applyRule(order, rule)).collect(Collectors.toList())
).collect(Collectors.flatMapping(Collection::stream, Collectors.toList()));

Nota: Collectors.flatMapping()é usado aqui em vez de simples flatMapexecutar o mapeamento plano de forma síncrona durante a coleta de fluxo.


Analógico Java 8:

List<AppliedRule> finalList = orders.parallelStream().map(order ->
        rules.stream().map(rule -> applyRule(order, rule)).collect(Collectors.toList())
).collect(Collectors.toList())
        .stream()
        .flatMap(Collection::stream)
        .collect(Collectors.toList());
Bananon
fonte
Obrigado pela resposta. Eu tentei sua abordagem e estou recebendo java.util.ConcurrentModificationException: null
mayank bisht
finalList = orders.parallelStream () .map (ordem -> rules.stream () .map (regra -> beanMapper.getBean (regra) .applyRule (createTemplate.apply (getMetaData.apply (regra), comando), ordem)) .collect (Collectors.toList ())). collect (Collectors.toList ()). stream (). flatMap (Collection :: stream) .collect (Collectors.toList ());
mayank bisht
@mayankbisht, isso significa que beanMapper.getBean(rule) .applyRule(createTemplate.apply(getMetaData.apply(rule), command), order)não é uma função pura, portanto não pode ser usada em paralelo. Tente remover todos os efeitos colaterais dele; ConcurrentModificationExceptiono rastreamento de pilha pode ajudar a localizá-los.
Bananon 25/12/19
0

Isso vai funcionar?

final int rulesSize = rules.size();
AtomicInteger atomicInteger = new AtomicInteger(0);
orders.stream().parallel().forEach(order -> {
    IntStream.range(0, rulesSize).parallel().forEach( i -> {
        synchronized (atomicInteger) {
            System.out.println(" Order : " + order + " rule :" + rules.get(atomicInteger.getAndIncrement() % rulesSize));
        }
    });
});

Resultado

 Order : order1 rule :rule1
 Order : order4 rule :rule2
 Order : order1 rule :rule3
 Order : order3 rule :rule1
 Order : order3 rule :rule2
 Order : order3 rule :rule3
 Order : order2 rule :rule1
 Order : order2 rule :rule2
 Order : order2 rule :rule3
 Order : order1 rule :rule1
 Order : order1 rule :rule2
 Order : order4 rule :rule3
 Order : order1 rule :rule1
 Order : order4 rule :rule2
 Order : order1 rule :rule3
BHAWANI SINGH
fonte
0

A ordem dos pedidos pode ser qualquer coisa, mas a ordem das regras não deve mudar. Também para uma regra de pedido específica, deve haver um grupo.

Se for esse o caso, não há espaço para o paralelismo real.

Quando

order1-rule1
order1-rule2
order2-rule1
order2-rule2

e

order2-rule1
order2-rule2
order1-rule1
order1-rule2

são as únicas execuções válidas para 2 pedidos e 2 regras,
e

order1-rule1
order2-rule1
order1-rule2
order2-rule2

é considerado inválido, isso não é paralelismo, apenas randomização de orders, presumivelmente sem ganho. Se você está "entediado" com order1a primeira vez o tempo todo, pode embaralhar a lista, mas isso é tudo:

public static void main (String[] args) throws java.lang.Exception
{
    List<String> orders = Arrays.asList("order1", "order2", "order3", "order4");
    List<String> rules = Arrays.asList("rule1", "rule2", "rule3");
    Collections.shuffle(orders);
    orders.forEach(order->{
        rules.forEach(rule->{
            System.out.println(order+"-"+rule);
        });
    });
}

Nem mesmo o streaming é necessário, apenas dois loops aninhados. Teste: https://ideone.com/qI3dqd

order2-rule1
order2-rule2
order2-rule3
order4-rule1
order4-rule2
order4-rule3
order1-rule1
order1-rule2
order1-rule3
order3-rule1
order3-rule2
order3-rule3


Resposta original

Mas está mudando a ordem rules.forEach (rule -> {}}.

Não, não tem. Os orders podem se sobrepor, mas a ordem de rules para cada pedido é mantida. Por que um não paralelo forEachfaria outra coisa?

Código de exemplo:

public static void main (String[] args) throws java.lang.Exception
{
    List<String> orders = Arrays.asList("order1", "order2", "order3", "order4");
    List<String> rules = Arrays.asList("rule1", "rule2", "rule3");
    orders.stream().parallel().forEach(order->{
        rules.forEach(rule->{
            System.out.println(order+"-"+rule);
        });
    });
}

Teste: https://ideone.com/95Cybg
Exemplo de saída:

order2-rule1
order2-rule2
order2-rule3
order1-rule1
order1-rule2
order1-rule3
order4-rule1
order4-rule2
order4-rule3
order3-rule1
order3-rule2
order3-rule3

A ordem de orders é mista, mas os rulesempre são 1-2-3. Acho que sua saída simplesmente ocultou os pares (na verdade você não mostrou como foi gerada).

É claro que ele pode ser estendido com alguns atrasos, portanto o processamento de orders se sobrepõe:

public static void delay(){
    try{
        Thread.sleep(ThreadLocalRandom.current().nextInt(100,300));
    }catch(Exception ex){}
}

public static void main (String[] args) throws java.lang.Exception
{
    List<String> orders = Arrays.asList("order1", "order2", "order3", "order4");
    List<String> rules = Arrays.asList("rule1", "rule2", "rule3");
    orders.stream().parallel().forEach(order->{
        rules.forEach(rule->{
            delay();
            System.out.println(order+"-"+rule);
        });
    });
}

Teste: https://ideone.com/cSFaqS
Exemplo de saída:

order3-rule1
order2-rule1
order2-rule2
order3-rule2
order3-rule3
order2-rule3
order1-rule1
order4-rule1
order1-rule2
order4-rule2
order4-rule3
order1-rule3

Isso pode ser algo que você viu, apenas sem a orderxparte. Com os orders visíveis, pode ser rastreado que rulecontinua chegando como 1-2-3, pororder . Além disso, sua lista de exemplos continha order1duas vezes, o que com certeza não ajudou a ver o que estava acontecendo.

tevemadar
fonte
Obrigado pela resposta. A saída acima pode estar correta para um número menor de pedidos. Mas se você aumentar os pedidos, obterá uma saída diferente. Por exemplo: (ordem4-regra1 ordem4-regra2 ordem4-regra1) (ordem1-regra1 ordem1-regra2) (ordem3-regra1 ordem3-regra2) (ordem4-regra1 ordem4-regra2 ordem4-regra1 ordem4-regra2).
mayank bisht
A ordem dos pedidos pode ser qualquer coisa, mas a ordem das regras não deve mudar. Também para uma regra de pedido específica, deve haver um grupo. Por ex. (order1- regra 1 order1-rule2 order1-rule3) e not (order1-rule1 order2-rule1 order1-rule2 order1-rule3).)
mayank bisht
@mayankbisht Acho que essas restrições simplesmente não permitem o processamento paralelo. Veja a resposta atualizada (escrevi a nova parte no início).
tevemadar 24/12/19
Sim, eu entendo isso, e foi por isso que postei essa pergunta aqui. Eu pensei que talvez haja outra maneira de fazer isso, ou talvez possamos mudar a algo
mayank bisht
@mayankbisht, você poderia descrever por que orders não podem se sobrepor ( ruletalvez sejam esses stateful e existam em um número limitado de cópias, talvez apenas uma única?). Mas geralmente não há paralelismo sem que as coisas funcionem paralelamente, afinal esse é o ponto principal do paralelismo.
tevemadar 24/12/19
0

Se você não se importa de tentar a biblioteca de terceiros. Aqui está um exemplo com minha biblioteca: abacus-util

StreamEx.of(orders).parallelStream().forEach(order -> {}}

E você pode até especificar o número da linha:

StreamEx.of(orders).parallelStream(maxThreadNum).forEach(order -> {}}

A ordem de ruleserá mantida.

A propósito, como está no fluxo paralelo, o código ...finalList.add(...provavelmente não funcionará. Eu acho que é melhor coletar o resultado para listar:

StreamEx.of(orders).parallelStream().map/flatMap(order -> {...}}.toList()

também é possível, mesmo que você queira manter a ordem orderpor algum motivo posteriormente:

StreamEx.of(orders).indexed().parallelStream()
      .map/flatMap(order -> {...}}.sortedBy(...index).toList()
user_3380739
fonte