O que é um bloqueio de spin
Falando em bloqueios de spin, precisamos começar com o mecanismo de bloqueio sob multi-threading. Como alguns recursos em um ambiente de sistema multiprocessador são limitados, eles às vezes exigem exclusão mútua. Neste momento, um mecanismo de bloqueio será introduzido. Somente o processo que adquire o bloqueio pode obter acesso a recursos. Ou seja, apenas um processo pode adquirir o bloqueio de cada vez para entrar em sua própria área crítica. Ao mesmo tempo, dois ou mais processos não podem entrar na área crítica. Ao sair da área crítica, o bloqueio será liberado.
Ao projetar um algoritmo mutex, você sempre enfrenta uma situação em que não tem uma trava, ou seja, o que você deve fazer se não conseguir uma trava?
Geralmente existem 2 maneiras de lidar com isso:
Uma é que o interlocutor que não obteve a fechadura está fazendo uma volta por lá para ver se o suporte do bloqueio de spin liberou a fechadura. Este é o foco deste artigo - Spin Lock. Ele não precisa bloquear a cidade da linha (não bloqueando).
Outra maneira é que o processo sem obter os blocos de bloqueio em si (bloqueando) e continue a executar outras tarefas no thread, que é o mutex (incluindo o bloqueio interno sincronizado, o reentrantlock etc.).
introdução
CAS (Compare e Swap), ou seja, comparação e troca, também é a operação principal que implementa o que geralmente chamamos de bloqueio de spin ou bloqueio otimista.
Sua implementação é muito simples, que é comparar um valor esperado com um valor de memória. Se os dois valores forem iguais, substitua o valor da memória pelo valor esperado e retorne true. Caso contrário, retorne FALSE.
Garanta operação atômica
Qualquer tecnologia surge para resolver certos problemas específicos. O problema que o CAS precisa resolver é garantir operações atômicas. O que é uma operação atômica? Os átomos são os menores e indecentes, e a operação atômica é a operação menor e indecente. Ou seja, uma vez que a operação inicie, ela não pode ser interrompida e sabe que a operação é concluída. Em um ambiente multithread, as operações atômicas são um meio importante para garantir a segurança da linha. Por exemplo, suponha que haja dois threads funcionando e desejam modificar um determinado valor. Pegue a operação de auto-incremento como exemplo. Para realizar a operação de auto-incremento em um número inteiro I, são necessárias três etapas básicas:
1. Leia o valor atual de i;
2. Adicione 1 ao valor i;
3. Escreva o valor de Valor de volta à memória;
Suponha que ambos os processos leiam o valor atual de I, assumindo que ele é 0, neste momento, o encadeamento A adiciona 1 a I, o encadeamento B também adiciona 1 e, finalmente, eu sou 1, não 2. Isso ocorre porque a operação de auto -increment não é uma operação atômica, e as três etapas divididas podem ser interferidas. Como no exemplo abaixo, para 10 threads, cada thread executa 10.000 operações de I ++, o valor esperado é de 100.000, mas, infelizmente, o resultado é sempre inferior a 100.000.
estático int i = 0; public static void add () {i ++; } classe estática privada Plus implementa runnable {@Override public void run () {for (int k = 0; k <10000; k ++) {add (); }}} public static void main (string [] args) lança interruptedException {thread [] threads = new Thread [10]; for (int i = 0; i <10; i ++) {threads [i] = new Thread (new Plus ()); threads [i] .start (); } para (int i = 0; i <10; i ++) {threads [i] .Join (); } System.out.println (i); }Nesse caso, o que devo fazer? É isso mesmo, talvez você já tenha pensado nisso, você pode bloquear ou usar a implementação sincronizada, por exemplo, modificar o método add () para o seguinte:
public sincronizado estático void add () {i ++; }Como alternativa, a operação de bloqueio é implementada, por exemplo, usando o ReentrantLock (ReentrantLock).
bloqueio estático privado = new reentrantlock (); public static void add () {Lock.lock (); i ++; Lock.Unlock (); } CAS implementos de bloqueio de spin
Como as operações atômicas podem ser implementadas usando a palavra -chave de bloqueio ou sincronizado, por que usar o CAS? Porque o bloqueio ou o uso de palavras -chave sincronizadas traz uma grande perda de desempenho, enquanto o uso do CAS pode obter o bloqueio otimista. Na verdade, ele utiliza diretamente as instruções no nível da CPU, portanto o desempenho é muito alto.
Como mencionado acima, o CAS é a base para a implementação de bloqueios de rotação. O CAS usa instruções da CPU para garantir a atomicidade da operação para alcançar o efeito de bloqueio. Quanto ao spin, também é muito claro ler o significado literal. Se você girar você mesmo, é um loop. Geralmente é implementado usando um loop infinito. Dessa forma, uma operação CAS é executada em um loop infinito. Quando a operação é bem -sucedida e retorna verdadeira, o loop termina; Quando falso, o loop é executado e a operação do CAS é continuada até que o verdadeiro seja retornado.
De fato, muitos lugares do JDK usam CAS, especialmente no pacote java.util.concurrent, como Countdownlatch, Semaphore, Reentrantlock e Java.util.concurrent.atomic. Eu acredito que todo mundo usou atômico*, como atomicbooliano, atomicinteger, etc.
Aqui tomamos atomicbooliano como exemplo, porque é simples o suficiente.
classe pública atomicboolian implementa java.io.Serializable {private estático final serialversionuid = 4654671469794556979l; // Configurar para usar o inseguro. privado estático final longo valueOffset; static {try {valueOffSet = insefa.ObjectFieldOffset (atomicboolean.class.getDecaredfield ("value")); } catch (Exceção ex) {lança novo erro (ex); }} private Value volátil int; public final boolean get () {retorna valor! = 0; } Public final boolean Comparansset (Boolean espera, atualização booleana) {int e = espera? 1: 0; int u = atualização? 1: 0; return insefa.compareandswapint (isto, valueoffset, e, u); }}Isso faz parte do Código de AtomicBoolean e vemos vários métodos e propriedades importantes aqui.
1. O objeto Sun.misc.unsfe é usado. Esta classe fornece uma série de métodos para operar diretamente objetos de memória, mas é usada apenas internamente pelo JDK e não é recomendada para os desenvolvedores usarem;
2. O valor representa o valor real. Você pode ver que o método GET realmente julga o valor booleano com base no fato de o valor ser igual a 0. O valor aqui é definido como volátil, porque o volátil pode garantir a visibilidade da memória, ou seja, desde que o valor mude, outros encadeamentos podem ver o valor alterado imediatamente. O próximo artigo falará sobre a visibilidade do volátil, bem -vindo a seguir
3. ValueOffset é o deslocamento da memória do valor do valor, obtido usando o método inseguro.ObjectFieldOffset e usado como o método compareandSet subsequente;
4. Método comparado, este é o método principal de implementar CAS. Ao usar o método AtomicBoolean, você só precisa passar o valor esperado e o valor a ser atualizado. O método insefe.compareandswapint (este, ValueOffset, e, u) é chamado. É um método nativo, implementado em C ++, e o código específico não será publicado. Em resumo, ele usa a instrução CMMPXCHG da CPU para concluir a comparação e a substituição. Obviamente, dependendo da versão específica do sistema, também existem diferenças na implementação. Aqueles que estão interessados podem procurar os artigos relevantes por si mesmos.
Use cenários
Por exemplo, o AtomicBoolean pode ser usado nesse cenário. O sistema precisa determinar se algumas operações de inicialização precisam ser realizadas com base nas propriedades do estado de uma variável booleana. Se for um ambiente multithread e evite execuções repetidas, ele pode ser implementado usando atomicBoolean. O pseudocódigo é o seguinte:
Sinalização estática final do atômico estático privado = new AtomicBoolean (); if (flag.compareandSet (false, true)) {init (); }Por exemplo, o AtomicInteger pode ser usado em contadores e em ambientes multithread para garantir uma contagem precisa.
Perguntas ABA
Há um problema com o CAS, que é que um valor muda de A para B e, em seguida, de B para A. Nesse caso, o CAS pensará que o valor não mudou, mas na verdade ele mudou. Nesse sentido, existe atômico -referência em pacotes simultâneos que fornecem uma implementação com base no número da versão, que pode resolver alguns problemas.
Resumir
O acima é o conteúdo inteiro deste artigo. Espero que o conteúdo deste artigo tenha certo valor de referência para o estudo ou trabalho de todos. Se você tiver alguma dúvida, pode deixar uma mensagem para se comunicar. Obrigado pelo seu apoio ao wulin.com.