Pergunta

Eu só percebi que eu preciso para sincronizar uma quantidade significativa de código de coleta de dados em um aspecto, mas o desempenho é uma preocupação real. Se o desempenho diminui muito a minha ferramenta vai ser jogado fora. Eu estarei escrevendo inteiros e longs individualmente e para vários arrays, ArrayLists e Maps. Haverá vários threads de uma aplicação que fará chamadas de função que vai ser pego pelo meu aspecto. Que tipo de coisas que eu deveria olhar para fora para que afetará negativamente o desempenho? Que padrões de código são mais eficientes?

Em particular, eu tenho um método que chama muitos outros dados métodos de gravação:

void foo() {
    bar();
    woz();
    ...
}

Os métodos na maior parte não adicionando um incremento de campos de aspecto

void bar() {
    f++; // f is a field of the aspect
    for (int i = 0; i < ary.length; i++) {
        // get some values from aspect point cut
        if (some condiction) {
            ary[i] += someValue; // ary a field of the aspect
        }
     }
 }

Devo sincronizar foo ou bar, Woz e outros individualmente, ou devo mover todo o código no bar, woz, etc em foo e apenas sincronizá-los? Devo sincronizar on this, em um objeto de sincronização especificamente criado:

private final Object syncObject = new Object();

(ver este post ), ou em elementos de dados individuais dentro dos métodos:

ArrayList<Integer> a = new ArrayList<Integer>();

void bar() {    
    synchronize(a) {
        // synchronized code
    }
}
Foi útil?

Solução

A concorrência é extremamente complicado. É muito fácil de errar, e muito difícil de acertar. Eu não seria terrivelmente preocupado com o desempenho neste momento. Minha primeira e principal preocupação seria a de obter o código concorrente para trabalhar com segurança (sem bloqueios ou condições de corrida).

Mas sobre a questão do desempenho: em caso de dúvida, perfil. É difícil dizer quão diferentes esquemas de sincronização irá afetar o desempenho. É ainda mais difícil para nós dar-lhe sugestões. Precisaríamos ver muito mais do seu código e ganhar uma compreensão mais profunda do que o aplicativo faz para lhe dar uma resposta verdadeiramente útil. Em contraste, profiling lhe dá provas concretas a respeito de se uma abordagem é mais lento do que o outro. Ele pode até mesmo ajudá-lo a identificar onde a desaceleração é.

Há um monte de grandes ferramentas de perfil para Java nos dias de hoje. Os profilers Netbeans e Eclipse são boas.

Além disso, eu recomendo ficar longe de sincronização matéria completamente. Tente usar algumas das classes no pacote java.util.concurrency. Eles fazem escrever código concorrente muito mais fácil e muito menos propenso a erros.

Além disso, eu recomendo que você leia Java Concurrency in Practice por Brian Goetz, et al. É muito bem escrito e cobre um lote de terreno.

Outras dicas

Regra de ouro não é para sincronizar em this - a maioria das vezes é um ponto de performance - todos os métodos são sincronizados em um objeto.

Considere o uso de fechaduras - they'a muito agradável abstração e muitos bons recursos como, tentando bloqueio para um período de tempo, e em seguida, dando-se:

if(commandsLock.tryLock(100, TimeUnit.MILLISECONDS)){
     try { 
         //Do something
     }finally{
          commandsLock.unlock();
     }
}else{
     //couldnt acquire lock for 100 ms
}   

Eu segunda opinião sobre o uso java.util.concurrent. Eu faria dois levls de sincronização

  • Acesso coleção sincronizar (se for necessário)
  • o acesso de campo Sincronizar

Acesso Collection

Se a sua coleção são read-only ou seja, sem elementos são removidos-inserido (mas elementos podem mudar) Gostaria de dizer que você deve usar sincronizados coleções (mas isso pode não ser necessário ...) e iterações Sincronizar não faça:

Leia apenas:

for (int i = 0; i < ary.length; i++) {
    // get some values from aspect point cut
    if (some condiction) {
        ary += someValue; // ary a field of the aspect
    }
 }

e ária é obtido por exemplo Collections.synchronizedList.

read-write

synchronized(ary){
    for (int i = 0; i < ary.length; i++) {
        // get some values from aspect point cut
        if (some condiction) {
            ary += someValue; // ary a field of the aspect
        }
     }
 }

Ou use algumas coleções simultâneas (como CopyOnWriteArrayList ) que é inherentently segura therad.

principal diferença é que - em primeira leitura apenas wersion qualquer número de segmentos podem interagir sobre este coleções, e em segundo lugar apenas um de cada vez pode iterate. Em ambos os casos apenas um therad em um momento deve incrementar qualquer campo determinado.

Acesso Campo

incrementations Sincronizar em campos separadamente de iterações sincronização.

como:

  Integer foo = ary.get(ii); 
  synchronized(foo){
      foo++;
  }

se livrar de sincronização

  1. Usar coleções simultâneas (de java.util.concurrent - não de `Collections.synchronizedXXX', este último necessidade ainda de sincronização na travessia).
  2. Use java.util.atomic que lhe permitem campos atomicamente incrememt.

Algo que você deve prestar atenção:

Java modelo de memória - sua uma conversa que dá muito bom entendimento sobre como sincronizações e aligment dados em JAVA funciona.

Upadte: desde escrever o abaixo, eu vejo que você atualizou a questão um pouco. Perdoe minha ignorance-- Eu não tenho idéia o que é um "aspecto" é-- mas a partir do código de exemplo que você postou, você também pode considerar o uso de atomics / coleções simultâneas (por exemplo AtomicInteger, AtomicIntegerArray) ou atualizadores de campo atômicas . Isto poderia significar um bom re-factoring do seu código, no entanto. (Em Java 5 sobre uma dupla-hyperthreading proc Xeon, o rendimento de AtomicIntegerArray é significativamente melhor do que uma matriz sincronizado;. desculpe, eu não tenho rodada para repetição do teste em mais procs / versão posterior JVM nota yet-- que o desempenho de 'sincronizado' melhorou desde então)

Sem mais informações específicas ou métricas sobre o seu programa particular, o melhor que você pode fazer é simplesmente siga boa concepção do programa . É importante notar que o desempenho e otimização de bloqueios de sincronização na JVM tem beed uma das áreas (se não, a área) que recebeu mais pesquisa e atenção ao longo dos últimos anos. E assim, as últimas versões de JVM do, não é tão ruim assim.

Assim, em geral, eu diria que Sincronizar minimamente sem "ficando louco" . Por 'minimamente', quero dizer para que você segurar o bloqueio durante o menor tempo possível, e de modo que apenas as partes que precisam usar que o uso de bloqueio específico que bloqueio específico. Mas só se a mudança é fácil de fazer e é fácil de provar que seu programa ainda está correto. Por exemplo, em vez de fazer isso:

synchronized (a) {
  doSomethingWith(a);
  longMethodNothingToDoWithA();
  doSomethingWith(a);
}

considerar fazer isso se e só se seu programa ainda será correto:

synchronized (a) {
  doSomethingWith(a);
}
longMethodNothingToDoWithA();
synchronized (a) {
  doSomethingWith(a);
}

Mas lembre-se, a atualização de campo simples estranho com um bloqueio mantido desnecessariamente provavelmente não vai fazer muita diferença tangível, e poderia realmente melhorar o desempenho. Às vezes, mantendo um bloqueio para um pouco mais e fazer menos bloqueio "housekeeping" pode ser benéfico. Mas a JVM pode fazer algumas dessas decisões , para que você não precisa ser tooo paranoid-- apenas fazer as coisas geralmente sensíveis e você deve ser fino.

Em geral, tentar e ter um bloqueio separado para cada conjunto de métodos / acessos que juntos formam um "processo independente". Fora isso, ter um objeto de bloqueio separado pode ser uma boa maneira de encapsulamento o bloqueio dentro da classe é usado por (ou seja, impedindo que ele seja usado por chamadores externos de uma forma que você não previu) , mas provavelmente não há diferença de desempenho per se de usar um objeto para outro, como o bloqueio (por exemplo, usando a instância própria vs um objeto privado declarou apenas para ser um bloqueio dentro dessa classe como você sugere), desde que os dois objetos de outra forma seriam usados exatamente da mesma maneira.

Deve haver uma diferença de desempenho entre um built-in construtor de linguagem e uma biblioteca, mas a experiência me ensinou a não acho que quando se trata de desempenho.

Se você compilar o aspecto para o aplicativo, em seguida, você terá basicamente nenhum impacto no desempenho, se você fazê-lo em tempo de execução (tipo de carga tecelagem), então você vai ver um impacto na performance.

Se você tem cada aspecto ser perinstance então ele pode reduzir a necessidade de sincronização.

Você deve ter o mínimo de sincronização possível, por tanto tempo, um curto possível, para reduzir quaisquer problemas.

Se possível, você pode querer compartilhar tão pouco estado possível entre tópicos, mantendo tanto local quanto possível, para reduzir quaisquer problemas de impasse.

Mais informação levaria a uma resposta melhor btw. :)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top