Prefácio
Revisei em março de 2016 e revisei por que preciso otimizar o código com base no meu próprio trabalho e na minha experiência diária de aprendizado. Antes de alterar, minha declaração era assim:
Assim como um camarão com baleias, talvez comer um ou dois camarões não seja muito eficaz para as baleias, mas se você comer muito camarão, as baleias estarão naturalmente cheias. A otimização de código é a mesma. Talvez uma ou duas otimizações sejam de pouco significado para melhorar a eficiência de execução do código, mas, desde que você possa prestar atenção à otimização do código em todos os lugares, geralmente é muito útil para melhorar a eficiência de execução do código.
Essa visão, na presente visão, é um motivo para a otimização de código, mas não está completamente correta. Hoje, com o desenvolvimento de tecnologia mecânica, os servidores geralmente têm 8 núcleos, 16 núcleos e CPUs de 64 bits, e a eficiência da execução do código é muito alta. O StringBuilder substitui StringBuffer e ArrayList substitui o Vector, o que melhora a eficiência da operação do código por minúsculo. Mesmo se você notar todos os pontos do projeto, não poderá ver nenhuma alteração óbvia na operação de código.
Eu acho que o papel mais importante da otimização do código deve ser: para evitar erros desconhecidos. Durante o código em execução on -line, muitos erros inesperados geralmente ocorrem, porque o ambiente on -line e o ambiente de desenvolvimento são muito diferentes, e o posicionamento de erros geralmente é um motivo muito pequeno. No entanto, para resolver esse erro, precisamos primeiro se auto-ver e empacotar o arquivo de classe a ser substituído, suspender a empresa e reiniciar. Para um projeto maduro, o último realmente tem um impacto muito grande, o que significa que os usuários não podem acessar o aplicativo durante esse período. Portanto, ao escrever código, prestar atenção a vários detalhes da fonte, pesando e usando as melhores opções, evitará erros desconhecidos e reduzirá bastante a carga de trabalho a longo prazo.
Os objetivos da otimização de código são:
1. Reduza o volume do código
2. Melhore a eficiência da operação de código
Alguns dos conteúdos deste artigo vêm da Internet e outros vêm do trabalho diário e do estudo. Claro, isso não é importante. O importante é se os detalhes dessas otimização de código são realmente úteis. Em seguida, este artigo será atualizado por um longo tempo. Desde que você encontre detalhes de otimização de código que vale a pena compartilhar, este artigo será atualizado de tempos em tempos.
Detalhes da otimização de código
(1) Tente especificar o modificador final da classe e método o máximo possível
Classes com modificadores finais não são deriváveis. Na API do Java Core, há muitos exemplos de aplicação final, como java.lang.string, e toda a classe é final. Especificar um modificador final para uma classe pode impedir que a classe seja herdada e a especificação de um modificador final para um método pode impedir que o método seja substituído. Se uma classe for especificada como final, todos os métodos dessa classe serão finais. O compilador Java procurará oportunidades para incluir todos os métodos finais. A inline é de grande importância para melhorar a eficiência de corrida de Java. Para detalhes, consulte o Otimização do tempo de execução do Java. Esse movimento pode melhorar o desempenho em uma média de 50%.
(2) Tente reutilizar objetos
Especialmente para o uso de objetos de string, o StringBuilder/StringBuffer deve ser usado quando ocorre a concatenação da String. Como as máquinas virtuais Java não apenas precisam gastar tempo gerando objetos, mas também podem precisar gastar tempo coletando e processando esses objetos no futuro, gerando muitos objetos terão um grande impacto no desempenho do programa.
(3) Use variáveis locais o máximo possível
Os parâmetros foram aprovados ao chamar o método e as variáveis temporárias criadas na chamada são armazenadas na pilha, que é mais rápida. Outras variáveis, como variáveis estáticas, variáveis de instância, etc., são todas criadas na pilha, que é mais lenta. Além disso, como as variáveis criadas na pilha estão finalizadas, esses conteúdos desaparecem e nenhuma coleta adicional de lixo é necessária.
(4) Feche o fluxo no tempo
Durante a programação Java, tenha cuidado ao executar a conexão com o banco de dados e as operações de streaming de E/S. Após o uso, feche -o a tempo de liberar recursos. Como a operação desses objetos grandes causará grandes despesas gerais do sistema e, se você não tomar cuidado, isso levará a sérias conseqüências.
(5) minimizar cálculos repetidos de variáveis
Para esclarecer um conceito, mesmo que exista apenas uma frase no método, ele ainda é consumido, incluindo a criação de quadros de pilha, protegendo o site ao chamar o método e restaurar o site ao chamar o método. Por exemplo, a seguinte operação:
for (int i = 0; i <list.size (); i ++) {...}Recomenda -se substituí -lo por:
for (int i = 0, comprimento = list.size (); i <comprimento; i ++) {...}Dessa forma, quando list.size () é muito grande, reduz muito consumo
(6) Tente adotar uma estratégia de carregamento preguiçosa, ou seja, crie -a quando necessário
Por exemplo:
String str = "aaa"; if (i == 1) {list.add (str);}Recomenda -se substituí -lo por:
if (i == 1) {string str = "aaa"; list.add (str);}(7) Use anormalidades com cautela
A anormalidade não é boa para o desempenho. Para fazer uma exceção, você deve primeiro criar um novo objeto. O construtor da interface jogável chama o método de sincronização local chamado FillInstackTrace (). O método FillInstackTrace () verifica a pilha e coleta informações de rastreamento de chamadas. Enquanto uma exceção for lançada, a máquina virtual Java deve ajustar a pilha de chamadas porque um novo objeto é criado durante o processamento. Exceções só podem ser usadas para manuseio de erros e não devem ser usadas para controlar o fluxo do programa.
(8) Não use tente ... pegar ... em loop, deve ser colocado na camada mais externa
De acordo com as opiniões apresentadas pelos internautas, acho que vale a pena discutir
(9) Se o comprimento do conteúdo a ser adicionado puder ser estimado, especifique o comprimento inicial para as classes de coleta e ferramentas implementadas em uma matriz na camada subjacente.
Por exemplo, ArrayList, Linkedllist, StringBuilder, StringBuffer, Hashmap, Hashset, etc. Tome StringBuilder como exemplo:
Stringbuilder () // alocar padrão 16 caracteres do Space StringBuilder (Int size) // Alocar padrão 16 caracteres do Space StringBuilder (String str) // Alocar padrão 16 caracteres + str.Length () Espaço de caractere
Sua capacidade de inicialização pode ser definida através do construtor da classe (aqui nos referimos não apenas ao StringBuilder acima), o que pode melhorar significativamente o desempenho. Por exemplo, o StringBuilder, o comprimento representa o número de caracteres que o atual StringBuilder pode manter. Como quando o StringBuilder atinge sua capacidade máxima, ele aumentará sua própria capacidade para 2 vezes e adiciona 2. Sempre que o StringBuilder atingir sua capacidade máxima, terá que criar uma nova matriz de caracteres e copiar o conteúdo da matriz antiga para a nova matriz de caracteres - esta é uma operação muito consumida por desempenho. Imagine, se você puder estimar que 5000 caracteres são armazenados na matriz de caracteres sem especificar o comprimento, o poder de 2 mais próximo de 5000 é 4096, e o 2 adicionado a cada expansão é independentemente dos 2, então:
Com base no 4096, solicite matrizes de caracteres de 8194 de tamanho, que somam as matrizes de caracteres de 12290 de tamanho de uma só vez. Se você pode especificar matrizes de caracteres de 5000 de tamanho 5.000 no início, economizará mais de duas vezes o espaço para copiar os caracteres 4096 originais para a nova matriz de caracteres.
Isso não apenas desperdiça o espaço de memória, mas também reduz a eficiência da operação do código. Portanto, não é errado definir uma capacidade de inicialização razoável para as classes de coleta e ferramentas implementadas na matriz subjacente, que trará resultados imediatos. No entanto, observe que coleções como o hashmap que são implementadas nas matrizes + listas vinculadas não devem definir o tamanho inicial do mesmo tamanho, porque a possibilidade de apenas um objeto conectado a uma tabela é quase 0. É recomendável definir o tamanho inicial para N POWER 2.
(10) Ao copiar uma grande quantidade de dados, use o comando System.arrayCopy ()
(11) Multiplicação e divisão usam operações de turno
Por exemplo:
for (val = 0; val <100000; val += 5) {a = val * 8; b = val / 2;}O uso de operações de turno pode melhorar bastante o desempenho, porque na parte inferior do computador, a operação de posicionamento é a mais conveniente e mais rápida, por isso é recomendável modificá -lo para:
for (val = 0; val <100000; val += 5) {a = val << 3; b = val >> 1;}Embora a operação de turno seja rápida, pode dificultar a compreensão do código, por isso é melhor adicionar comentários correspondentes.
(12) Não crie constantemente referências de objetos no loop
Por exemplo:
for (int i = 1; i <= count; i ++) {objeto obj = new Object (); }Essa abordagem fará com que a referência do objeto de contagem exista na memória. Se a contagem for grande, ela consumirá memória. É recomendável alterá -lo para:
Objeto obj = null; para (int i = 0; i <= count; i ++) {obj = new Object ();}Dessa maneira, existe apenas um objeto de referência de objeto na memória. Toda vez que o novo objeto () é usado, o objeto de objeto aponta para um objeto diferente, mas existe apenas um objeto na memória, que salva muito o espaço de memória.
(13) Com base na consideração da eficiência e da verificação do tipo, a matriz deve ser usada o máximo possível. Arraylist deve ser usado apenas se o tamanho da matriz não puder ser determinado.
(14) Tente usar o Hashmap, ArrayList e Stringbuilder. A menos que seja necessário para a segurança do encadeamento, não é recomendável usar hashtable, vetor e stringbuffer. Os três últimos têm sobrecarga de desempenho devido ao uso de mecanismos de sincronização.
(15) Não declare matrizes como final estático público
Como isso não faz sentido, ele define apenas a referência como final estática, e o conteúdo da matriz ainda pode ser alterado à vontade. Declarar a matriz como público é uma vulnerabilidade de segurança, o que significa que a matriz pode ser alterada por classes externas
(16) Tente usar casos únicos em ocasiões adequadas
O uso de singletons pode reduzir a carga de carga, reduzir o tempo de carregamento e melhorar a eficiência de carregamento, mas nem todos os lugares são adequados para singletons. Simplificando, os singletons são aplicáveis principalmente aos três aspectos a seguir:
Controle o uso de recursos, controle a geração de instâncias simultâneas de controle de acesso de recursos por meio da sincronização do encadeamento, de modo a alcançar o objetivo de salvar recursos para controlar o compartilhamento de dados e permitir a comunicação entre vários processos não relacionados ou threads sem estabelecer associações diretas.
(17) Tente evitar o uso de variáveis estáticas à vontade
Você deve saber que, quando um objeto é referenciado por uma variável definida como uma estática, o GC geralmente não recicla a memória da pilha ocupada pelo objeto, como:
classe pública A {private estático b b = new B (); }Neste momento, o ciclo de vida da variável estática B é o mesmo da classe A. Se a classe A não for desinstalada, o objeto B apontado pela referência B residirá na memória até que o programa termine
(18) Limpe as sessões que não são mais necessárias no tempo
Para limpar as sessões que não estão mais ativas, muitos servidores de aplicativos têm um tempo limite de sessão padrão, geralmente 30 minutos. Quando o servidor de aplicativos precisa salvar mais sessões, se houver memória insuficiente, o sistema operacional transferirá parte dos dados para o disco. O servidor de aplicativos também pode despejar algumas sessões inativas em disco de acordo com o algoritmo MRU (mais frequentemente usado recentemente) e pode até lançar exceções insuficientes de memória. Se a sessão for despejada no disco, ela deve ser serializada primeiro. Em aglomerados em larga escala, os objetos serializados são caros. Portanto, quando a sessão não for mais necessária, o método Invalidate () de HttpSession deve ser chamado a tempo para limpar a sessão.
(19) Uma coleção que implementa a interface aleatória, como a ArrayList, deve usar o mais comum para loop em vez de foreach loop para atravessar
Isso é recomendado pelo JDK para os usuários. A explicação da API da JDK sobre a interface RandomAccess é: implementar a interface RandomAccess é usada para indicar que ela suporta acesso aleatório rápido. O principal objetivo dessa interface é permitir que os algoritmos gerais alterem seu comportamento, para que possa fornecer um bom desempenho quando aplicado a listas de acesso aleatório ou contínuo. A experiência prática mostra que, se as instâncias de classe que implementam a interface aleatória forem acessadas aleatoriamente, a eficiência do uso de loops ordinárias será maior que a de usar loops foreach; Por outro lado, se acessado sequencialmente, será mais eficiente usar o iterador. Você pode usar códigos semelhantes aos seguintes para fazer julgamentos:
if (list a instância de aleatoriamente acresced) {for (int i = 0; i <list.size (); i ++) {}} else {iterator <?> iterator = list.iterable (); while (iterator.hasnext ()) {iterator.Next ()}}O princípio de implementação subjacente do loop foreach é o iterador, consulte Java Sintaxe Açúcar 1: Parâmetros de comprimento variável e o princípio do loop foreach. Portanto, a segunda metade da frase "inversamente, se for acessada sequencialmente, o uso do iterador será mais eficiente" significa que as instâncias de classe que são acessadas sequencialmente são percorridas usando um loop foreach.
(20) Use blocos de código síncrono em vez do método de sincronização
Isso já está claramente declarado no bloco de métodos de bloqueio sincronizado do artigo no módulo multithread. A menos que possa ser determinado que todo o método precisa ser sincronizado, tente usar blocos de código sincronizado para evitar sincronizar os códigos que não precisam ser sincronizados, o que afeta a eficiência da execução do código.
(21) declare a constante como final estática e nomeie -a em capital
Dessa forma, esses conteúdos podem ser colocados no pool constante durante a compilação, evitando o cálculo dos valores constantes gerados durante o tempo de execução. Além disso, nomear o nome de uma constante em capital também pode facilitar a distinção entre constantes e variáveis
(22) Não crie alguns objetos não utilizados, não importe algumas classes não utilizadas
Isso não faz sentido. Se "o valor da variável local I não é usado" e "o importe java.util nunca é usado" aparecer no código, exclua esses conteúdos inúteis
(23) Evite usar a reflexão durante a operação do programa
Para mais informações, consulte Reflexão. A reflexão é uma função muito poderosa fornecida pelo Java aos usuários. Funções poderosas geralmente significam baixa eficiência. Não é recomendável usar o mecanismo de reflexão com frequência durante a operação do programa, especialmente o método de invasão do método. Se for realmente necessário, uma abordagem sugestiva é instanciar um objeto através da reflexão e colocá -lo na memória quando o projeto é iniciado. O usuário só se preocupa com a velocidade de resposta mais rápida ao interagir com o par e não se importa com quanto tempo leva para o início do projeto de pares.
(24) Use pool de conexão do banco de dados e pool de threads
Ambas as piscinas são usadas para reutilizar objetos, o primeiro evita a abertura e o fechamento frequentes de conexões, e o último evita a criação e destruição frequentes de threads
(25) Use fluxos de entrada e saída em buffer para operações de IO
Os fluxos de entrada e saída em buffer, nomeadamente BufferredReader, BufferWriter, BufferErnputStream, BufferedOutputStream, que podem melhorar bastante a eficiência de IO
(26) Use o ArrayList para cenas com mais inserção seqüencial e acesso aleatório e use o LinkedList para cenas com mais exclusão de elementos e inserção intermediária.
Isso é conhecido pela compreensão dos princípios da ArrayList e LinkedList
(27) Não deixe muitos parâmetros formais no método público
O método público é um método fornecido ao mundo exterior. Se você fornecer a esses métodos muitos parâmetros formais, há duas principais desvantagens:
Violar a ideia de programação orientada a objetos. Java enfatiza que tudo é um objeto. Muitos parâmetros formais e não corresponde à ideia de programação orientada a objetos. Muitos parâmetros inevitavelmente levarão a um aumento na probabilidade de erro nas chamadas de método.
Quanto a quantos "demais" se referem, 3 ou 4. Por exemplo, usamos o JDBC para escrever um método InsertStudentInfo. Existem 10 campos de informação do aluno a serem inseridos na tabela do aluno. Esses 10 parâmetros podem ser encapsulados em uma classe de entidade como parâmetros formais do método de inserção.
(28) Quando variáveis de string e constantes de string são escritas na frente de constantes de string
Este é um truque relativamente comum, se houver o seguinte código:
String str = "123"; if (str.equals ("123")) {...}Recomenda -se modificá -lo para:
String str = "123"; if ("123" .equals (str)) {...}Isso pode evitar principalmente exceções nulas de ponteiro
(29) Por favor, saiba que em Java não há diferença entre se (i == 1) e se (1 == i), mas da perspectiva dos hábitos de leitura, é recomendável usar o primeiro
Às vezes, as pessoas perguntam se há alguma diferença entre "se (i == 1)" e "if (1 == i)"? Isso começa com C/C ++.
Em C/C ++, a condição de julgamento "if (i == 1)" é válida e é baseada em 0 e não 0. 0 significa falso e não-0 significa verdadeiro. Se houver um pedaço de código:
int i = 2; if (i == 1) {...} else {...}C/C ++ juíz que "i == 1" não é válido, por isso é representado por 0, isto é, falso. Mas se:
int i = 2; if (i = 1) {...} else {...}Se o programador gravar acidentalmente "se (i == 1)" como "if (i = 1)", haverá um problema. Atribua I a 1 dentro se. Se você determinar que o conteúdo não é 0, o retorno true, mas eu é obviamente 2 e o valor comparado é 1, o falso que deve ser retornado. É muito provável que essa situação ocorra no desenvolvimento de C/C ++ e causará alguns erros difíceis de entender. Portanto, para evitar operações de atribuição incorretas dos desenvolvedores nas declarações IF, é recomendável escrever a instrução IF como:
int i = 2; if (1 == i) {...} else {...}Dessa forma, mesmo que o desenvolvedor grava acidentalmente "1 = i", o compilador C/C ++ pode verificar o mais rápido possível, porque podemos atribuir um valor variável i a 1, mas não podemos atribuir um valor constante 1 a i.
No entanto, em Java, é impossível aparecer a sintaxe "if (i = 1)" de C/C ++, porque uma vez que essa sintaxe for gravada, o Java compilará e relatará um erro "Tipo de incompatibilidade: não pode ser convertido de int para booleano". No entanto, embora não haja diferença semântica entre "if (i == 1)" e "if (1 == i)" em java, seria melhor recomendar o uso do primeiro em termos de hábitos de leitura.
(30) não use o método ToString () na matriz
Vamos dar uma olhada no que é impresso usando o tostring () para matrizes:
int i = 2; if (1 == i) {...} else {...}vire para fora:
I@18A992f
A intenção original é imprimir o conteúdo da matriz, mas pode causar uma exceção de ponteiro nulo porque a referência da matriz é nula. No entanto, embora não faça sentido para a matriz ToString (), o conteúdo da coleção pode ser impresso para a coleção ToString (), porque a classe pai da coleção, o método ToString () do objeto é reescrito.
(31) Não faça transformações de força descendente nos tipos de dados básicos que estão fora de alcance
Isso nunca obterá o resultado desejado:
public static void main (string [] args) {long l = 12345678901234l; int i = (int) l; System.out.println (i);}Podemos esperar obter alguns deles, mas o resultado é:
1942892530
Explique isso. Long in java tem 8 bytes com 64 bits; portanto, a representação de 12345678901234 no computador deve ser:
0000 0000 0000 0000 0000 1011 0011 1010 0111 0011 1100 1110 0010 1111 1111 0010
Um tipo de tipo int é de 4 bytes e 32 bits. Os primeiros 32 bits da sequência de dados binários acima são retirados da parte baixa:
0111 0011 1100 1110 0010 1111 1111 0010
Essa sequência de binários é representada como decimal 1942892530, por isso é a saída de conteúdo no console acima. A partir deste exemplo, podemos tirar duas conclusões:
1. O tipo de dados padrão do tipo inteiro é int, Long L = 12345678901234L. Esse número excedeu o intervalo de int, então há um L no final, indicando que esse é um número longo do tipo. A propósito, o tipo padrão de tipo de ponto flutuante é o dobro; portanto, ao definir flutuação, deve ser escrito como "" Float f = 3,5f "
2. Em seguida, escreva outra frase "int ii = l + i;" e relatará um erro porque Long + Int é um longo e não pode ser atribuído a int
(32) Os dados não usados na aula de coleta pública devem ser removidos no tempo
Se uma classe de coleção for pública (ou seja, não é uma propriedade em um método), os elementos desta coleção não serão lançados automaticamente porque sempre há referências apontando para eles. Portanto, se determinados dados da coleção pública não forem usados e não forem removidos, isso fará com que a coleção pública continue a crescer, fazendo com que o sistema tenha o potencial de vazamento de memória.
(33) converter um tipo de dados básico em uma string. O tipo de dados básico.ToString () é a maneira mais rápida, seguida por string.valueof (dados) e dados +"" é o mais lento
Geralmente, existem três maneiras de converter um tipo de dados básico para. Eu tenho dados do tipo inteiro, isto é, ToString (), String.valueof (i) e i+"" três maneiras. Quão eficientes são as três maneiras? Veja um teste:
public static void main (string [] args) {int looptime = 50000; Número inteiro i = 0; long startTime = System.currenttimemillis (); for (int j = 0; j <looptime; j ++) {string str = string.valueof (i); } System.out.println ("string.valueof ():" + (system.currenttimemillis () - starttime) + "ms"); startTime = system.currenttimemillis (); for (int j = 0; j <looptime; j ++) {string str = i.toString (); } System.out.println ("Integer.toString ():" + (System.CurrentTimeMillis () - StartTime) + "MS"); startTime = system.currenttimemillis (); for (int j = 0; j <looptime; j ++) {string str = i+""; } System.out.println ("i + /" /":" + (System.currenttimemillis () - starttime) + "ms");}O resultado em execução é:
String.valueof (): 11minteger.toString (): 5ms + "": 25ms
Portanto, quando você converte um tipo de dados básico em string no futuro, você deve priorizar o uso do método ToString (). Quanto ao porquê, é muito simples:
String.valueOf() é chamado Integer.toString() , mas será curto para julgar Integer.toString() antes de ligar. Vou chamar de i + "" A camada inferior usa a implementação do StringBuilder. Primeiro, use o método Apênd para secá -lo e, em seguida, use o método ToString () para obter a string.
Comparado com os três, é obviamente o mais rápido, o mais rápido, o mais lento
(34) Use a maneira mais eficiente de atravessar o mapa
Existem muitas maneiras de atravessar o mapa. Geralmente, o que precisamos para atravessar a chave e o valor no mapa. O método recomendado e mais eficiente é:
public static void main (string [] args) {hashmap <string, string> hm = new hashmap <string, string> (); hm.put ("111", "222"); Set <pap.entry <string, string >> entradas = hm.entrySet (); Iterator <map.entry <string, string >> iter = ingresset.iterator (); while (iter.hasnext ()) {map.entry <string, string> entradas = iter.next (); System.out.println (entradas.getKey () + "/t" + entrada.getValue ()); }} Se você deseja apenas iterar o valor da chave deste mapa, seria mais apropriado usar " Set<String> keySet = hm.keySet();"
(35) Recomenda -se operar separadamente para fechar () de recursos
Isso significa, por exemplo, eu tenho um código como este:
tente {xxx.close (); Aaaaa.close ();} catch (Exceção e) {...}Recomenda -se modificá -lo para:
tente {xxx.close ();} catch (Exceção e) {...} tente {yyy.close ();} catch (Exceção e) {...}Embora seja um pouco problemático, pode evitar vazamentos de recursos. Pensamos que, se não houver código modificado, se xxx.close () lançar uma exceção, ele entrará no bloco de captura. Aaaaa.close () não será executada, e o recurso AAYY não será reciclado e será ocupado o tempo todo. Com mais códigos como esse, isso pode fazer com que o identificador de recursos vazasse. Depois de mudar para o método de escrita a seguir, é garantido que XXX e AAAA serão fechados, não importa o quê.
(36) Para Threadlocal, você deve remover antes ou depois do uso
Atualmente, basicamente todos os projetos usam a tecnologia de pool de threads, o que é muito bom. Você pode configurar dinamicamente o número de threads e threads de reutilização.
No entanto, se você usar threadlocal em seu projeto, lembre -se de removê -lo antes ou depois do uso. Isso ocorre porque a tecnologia do pool de threads mencionada acima é reutilizar um thread, o que significa que, durante o código em execução, um thread não será destruído e estará esperando o próximo uso. Vamos dar uma olhada na referência na aula de threads que contém Threadlocal.threadlocalmap:
/* Valores ThreadLocal referentes a este thread. Este mapa é mantido * pela classe Threadlocal. */Threadlocal.threadlocalmap threadlocals = null;
O thread não é destruído significa que os dados no ThreadLocal.ThreadLocalmap do conjunto de threads anterior ainda existe. Quando o próximo thread reutiliza esse thread, é provável que o que você obtém seja os dados do conjunto de threads anteriores, em vez do conteúdo que você deseja.
Esse problema é muito obscuro. Depois que os erros causados por essa causa ocorrem, é muito difícil encontrar esse problema sem experiência relevante ou uma base sólida. Portanto, você deve prestar atenção a isso ao escrever código, o que reduzirá sua carga de trabalho subsequente.
(37) Lembre -se de substituir o número do diabo por definição constante. A existência do número do diabo reduzirá bastante a legibilidade do código. Se as constantes de string usam definições constantes podem depender da situação.
(38) Quando a atribuição inicial de longo ou longo
(39) Todos os métodos de reescrita devem manter a anotação @Override
Existem três razões para fazer isso:
(1) É claro que este método é herdado da classe pai
(2) métodos getObject () e get0bject (). A quarta letra do primeiro é "O", e o quarto pai e filho deste último é "0". Adicionar a anotação @Override pode determinar imediatamente se a reescrita é bem -sucedida.
(3) Modificar a assinatura do método na classe abstrata e a classe de implementação relatará imediatamente um erro de compilação.
(40) Recomenda -se usar a classe de ferramentas de objetos recém -introduzida no JDK7 para comparar objetos iguais, diretamente A.Equals (B), e há um risco de exceções de ponteiro nulo.
(41) Não use "+" para unir cordas no corpo do loop, mas use StringBuilder para anexar continuamente
Deixe -me falar sobre o motivo pelo qual não uso "+" para splicing de cordas. Se eu tiver um método:
public string appendtr (string orist, string ... appendtrs) {if (appendStrs == null || appendtrs.length == 0) {return oristr; } para (string appendtr: appendtrs) {orist += appendndtr; } retornar orist;}Depois de compilar este código, descompile o arquivo .class usando Javap -C para interceptar a parte principal:
Isso significa que toda vez que a máquina virtual encontra o operador "+" para unir a string, ele será um novo StringBuilder, depois chamará o método Apênd e, finalmente, chamará o método ToString () para converter a sequência no objeto ORIST. Ou seja, quantas vezes o loop será novo stringbuilder (), que é um desperdício de memória.
(42) Não captura a classe de exceção de tempo de execução definida na biblioteca de classes Java herdada da RunTimeException
A eficiência de manuseio de exceção é baixa, a maioria das classes de exceção de tempo de execução da RunTimeException pode ser completamente evitada por programadores, como:
(43) Evite usar instâncias aleatórias por vários threads. Embora o compartilhamento da instância seja seguro, ela causará a degradação do desempenho devido à concorrência pela mesma semente. Após o JDK7, você pode usar o Threadlocalrandom para obter números aleatórios
Explique a razão pela qual a concorrência pela mesma semente causa degradação do desempenho. Por exemplo, dê uma olhada na implementação do método nextInt () da classe aleatória:
public int nextInt () {return a seguir (32); }O próximo método (int bits) é chamado, que é um método protegido:
protegido int próximo (int bits) {Long Oldseed, Nextseed; Semente atômica = this.seed; do {Oldseed = SEED.Get (); NextSeed = (Multiplicador antigo * Multiplicador + adesão) e máscara; } while (! SEED.COMPAREANDSET (semeado, NextSeed)); Return (int) (NextSeed >>> (48 - bits));}E a semente aqui é uma variável global:
/*** O estado interno associado a este gerador de números de pseudorandom. * (As especificações dos métodos nesta classe descrevem a computação contínua * desse valor.) */ Semente atômica final privada;
Quando vários threads obtêm números aleatórios ao mesmo tempo, eles competem pela mesma semente, resultando em uma diminuição da eficiência.
(44) Classes estáticas, aulas de singleton e aulas de fábrica definem seus construtores para privados
Isso ocorre porque não precisamos novos para fora. Depois de definir o construtor como privado, garantimos que essas classes não produza objetos de instância.
PostScript
Excelente código vem de todas as pequenas otimização. Prestar atenção a todos os detalhes pode não apenas melhorar a eficiência de corrida do programa, mas também evitar muitos problemas desconhecidos.
Os acima são 44 sugestões de otimização de código Java introduzidas pelo editor. Espero que seja útil para você. Se você tiver alguma dúvida, deixe -me uma mensagem e o editor responderá a você a tempo. Muito obrigado pelo seu apoio ao site wulin.com!