Os dados compartilhados são um dos recursos mais críticos dos programas simultâneos. Este é um aspecto muito importante, seja um objeto que herda a classe Thread ou um objeto que implementa a interface Runnable.
Se você criar um objeto de uma classe que implemente a interface Runnable e usar esse objeto para iniciar uma série de threads, todos esses threads compartilharão as mesmas propriedades. Em outras palavras, se um thread modificar uma propriedade, todos os threads restantes serão afetados pela alteração.
Às vezes, preferimos usá-lo sozinho dentro de um thread em vez de compartilhá-lo com outros threads iniciados com o mesmo objeto. A interface de simultaneidade Java fornece um mecanismo muito claro para atender a esse requisito, chamado de variáveis locais de thread. O desempenho deste mecanismo também é impressionante.
saiba disso
Siga as etapas mostradas abaixo para concluir o programa de amostra.
1. Primeiro, implemente um programa com os problemas acima. Crie uma classe chamada UnsafeTask e implemente a interface Runnable. Declare uma propriedade privada do tipo java.util.Date na classe. O código é o seguinte:
Copie o código do código da seguinte forma:
classe pública UnsafeTask implementa Runnable {
data privada datainicial;
2. Implemente o método run() de UnsafeTask, que instancia o atributo startDate e envia seu valor para o console. Durma por um período aleatório e, em seguida, imprima o valor do atributo startDate no console novamente. O código é o seguinte:
Copie o código do código da seguinte forma:
@Substituir
execução void pública() {
datainicial = new Data();
System.out.printf("Tópico inicial: %s: %s/n",
Thread.currentThread().getId(), startDate);
tentar {
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Tópico finalizado: %s: %s/n",
Thread.currentThread().getId(), startDate);
}
3. Implemente a classe principal do programa problemático. Crie uma classe com um método main(), UnsafeMain. No método main(), crie um objeto UnsafeTask e use este objeto para criar 10 objetos Thread para iniciar 10 threads. No meio de cada thread, durma por 2 segundos. O código é o seguinte:
Copie o código do código da seguinte forma:
classe pública UnsafeMain {
public static void main(String[] args) {
tarefa UnsafeTask = new UnsafeTask();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(tarefa);
thread.start();
tentar {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
4. Pela lógica acima, cada thread tem um tempo de inicialização diferente. No entanto, de acordo com o log de saída abaixo, existem muitos valores de tempo idênticos. do seguinte modo:
Copie o código do código da seguinte forma:
Tópico inicial: 9: domingo, 29 de setembro 23:31:08 CST 2013
Tópico inicial: 10: domingo, 29 de setembro 23:31:10 CST 2013
Tópico inicial: 11: domingo, 29 de setembro 23:31:12 CST 2013
Tópico inicial: 12: domingo, 29 de setembro 23:31:14 CST 2013
Tópico finalizado: 9: domingo, 29 de setembro 23:31:14 CST 2013
Tópico inicial: 13: domingo, 29 de setembro 23:31:16 CST 2013
Tópico finalizado: 10: domingo, 29 de setembro 23:31:16 CST 2013
Tópico inicial: 14: domingo, 29 de setembro 23:31:18 CST 2013
Tópico finalizado: 11: domingo, 29 de setembro 23:31:18 CST 2013
Tópico inicial: 15: domingo, 29 de setembro 23:31:20 CST 2013
Tópico finalizado: 12: domingo, 29 de setembro 23:31:20 CST 2013
Tópico inicial: 16: domingo, 29 de setembro 23:31:22 CST 2013
Tópico inicial: 17: domingo, 29 de setembro 23:31:24 CST 2013
Tópico finalizado: 17: domingo, 29 de setembro 23:31:24 CST 2013
Tópico finalizado: 15: domingo, 29 de setembro 23:31:24 CST 2013
Tópico finalizado: 13: domingo, 29 de setembro 23:31:24 CST 2013
Tópico inicial: 18: domingo, 29 de setembro 23:31:26 CST 2013
Tópico finalizado: 14: domingo, 29 de setembro 23:31:26 CST 2013
Tópico finalizado: 18: domingo, 29 de setembro 23:31:26 CST 2013
Tópico finalizado: 16: domingo, 29 de setembro 23:31:26 CST 2013
5. Conforme mostrado acima, usaremos o mecanismo de variáveis locais de thread para resolver este problema.
6. Crie uma classe chamada SafeTask e implemente a interface Runnable. O código é o seguinte:
Copie o código do código da seguinte forma:
classe pública SafeTask implementa Runnable {
7. Declare um objeto do tipo ThreadLocal<Date> Quando o objeto é instanciado, o método inicialValue() é substituído e o valor da data real é retornado neste método. O código é o seguinte:
Copie o código do código da seguinte forma:
private static ThreadLocal<Data> startDate = novo
ThreadLocal<Data>() {
@Substituir
Data protegida valorinicial() {
retornar nova Data();
}
};
8. Implemente o método run() da classe SafeTask. Este método é igual ao método run() de UnsafeTask, exceto que o método do atributo startDate é ligeiramente ajustado. O código é o seguinte:
Copie o código do código da seguinte forma:
@Substituir
execução void pública() {
System.out.printf("Tópico inicial: %s: %s/n",
Thread.currentThread().getId(), startDate.get());
tentar {
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Tópico finalizado: %s: %s/n",
Thread.currentThread().getId(), startDate.get());
}
9. A classe principal deste exemplo seguro é basicamente a mesma classe principal do programa não seguro, exceto que UnsafeTask precisa ser modificado para SafeTask. O código específico é o seguinte:
Copie o código do código da seguinte forma:
classe pública SafeMain {
public static void main(String[] args) {
Tarefa SafeTask = new SafeTask();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(tarefa);
thread.start();
tentar {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
10. Execute o programa e analise as diferenças entre as duas entradas.
Para padronizar a nomenclatura das classes, a nomenclatura da classe principal neste artigo é um pouco diferente do texto original. Além disso, o programa original e a descrição do texto são inconsistentes. Deve ser um erro administrativo.
sabe por que
Abaixo está o resultado da execução do exemplo de segurança. A partir dos resultados, pode-se ver facilmente que cada thread possui um valor de atributo startDate pertencente ao respectivo thread. A entrada do programa é a seguinte:
Copie o código do código da seguinte forma:
Tópico inicial: 9: domingo, 29 de setembro 23:52:17 CST 2013
Tópico inicial: 10: domingo, 29 de setembro 23:52:19 CST 2013
Tópico inicial: 11: domingo, 29 de setembro 23:52:21 CST 2013
Tópico finalizado: 10: domingo, 29 de setembro 23:52:19 CST 2013
Tópico inicial: 12: domingo, 29 de setembro 23:52:23 CST 2013
Tópico finalizado: 11: domingo, 29 de setembro 23:52:21 CST 2013
Tópico inicial: 13: domingo, 29 de setembro 23:52:25 CST 2013
Tópico finalizado: 9: domingo, 29 de setembro 23:52:17 CST 2013
Tópico inicial: 14: domingo, 29 de setembro 23:52:27 CST 2013
Tópico inicial: 15: domingo, 29 de setembro 23:52:29 CST 2013
Tópico finalizado: 13: domingo, 29 de setembro 23:52:25 CST 2013
Tópico inicial: 16: domingo, 29 de setembro 23:52:31 CST 2013
Tópico finalizado: 14: domingo, 29 de setembro 23:52:27 CST 2013
Tópico inicial: 17: domingo, 29 de setembro 23:52:33 CST 2013
Tópico finalizado: 12: domingo, 29 de setembro 23:52:23 CST 2013
Tópico finalizado: 16: domingo, 29 de setembro 23:52:31 CST 2013
Tópico finalizado: 15: domingo, 29 de setembro 23:52:29 CST 2013
Tópico inicial: 18: domingo, 29 de setembro 23:52:35 CST 2013
Tópico finalizado: 17: domingo, 29 de setembro 23:52:33 CST 2013
Tópico finalizado: 18: domingo, 29 de setembro 23:52:35 CST 2013
Variáveis locais de thread armazenam uma cópia da propriedade para cada thread. Você pode usar o método get() do ThreadLocal para obter o valor de uma variável e usar o método set() para definir o valor de uma variável. Se uma variável local de thread for acessada pela primeira vez e um valor ainda não tiver sido atribuído à variável, o método inicialValue() será chamado para inicializar um valor para cada thread.
sem fim
A classe ThreadLocal também fornece o método remove() para excluir o valor da variável local armazenado no thread que chama esse método.
Além disso, a API de simultaneidade Java também fornece a classe InheritableThreadLocal, que permite que o thread filho receba os valores iniciais de todas as variáveis locais do thread herdável para obter o valor pertencente ao thread pai. Se o thread A tiver uma variável local de thread, quando o thread A criar o thread B, o thread B terá a mesma variável local de thread que o thread A. Você também pode substituir childValue() para inicializar as variáveis locais do thread filho. Este método aceitará o valor de uma variável local do thread passada como parâmetro do thread pai.
Usar doutrina
Este artigo foi traduzido do "Java 7 Concurrency Cookbook" (D Gua Ge o roubou como "Java7 Concurrency Sample Collection") e é usado apenas como material de aprendizagem. Não pode ser utilizado para quaisquer fins comerciais sem autorização.
Pequeno sucesso
Abaixo está uma versão completa de todo o código incluído nos exemplos desta seção.
Código completo da classe UnsafeTask:
Copie o código do código da seguinte forma:
pacote com.diguage.books.concurrencycookbook.chapter1.recipe9;
importar java.util.Date;
importar java.util.concurrent.TimeUnit;
/**
* Exemplos em que a segurança do thread não pode ser garantida
* Data: 23/09/2013
* Horário: 23h58
*/
classe pública UnsafeTask implementa Runnable {
data privada datainicial;
@Substituir
execução void pública() {
datainicial = new Data();
System.out.printf("Tópico inicial: %s: %s/n",
Thread.currentThread().getId(), startDate);
tentar {
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Tópico finalizado: %s: %s/n",
Thread.currentThread().getId(), startDate);
}
}
Código completo da classe UnsafeMain:
Copie o código do código da seguinte forma:
pacote com.diguage.books.concurrencycookbook.chapter1.recipe9;
importar java.util.concurrent.TimeUnit;
/**
* Exemplo de thread inseguro
* Data: 24/09/2013
* Horário: 00h04
*/
classe pública UnsafeMain {
public static void main(String[] args) {
tarefa UnsafeTask = new UnsafeTask();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(tarefa);
thread.start();
tentar {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Código completo da classe SafeTask:
Copie o código do código da seguinte forma:
pacote com.diguage.books.concurrencycookbook.chapter1.recipe9;
importar java.util.Date;
importar java.util.concurrent.TimeUnit;
/**
* Use variáveis locais de thread para garantir a segurança do thread
* Data: 29/09/2013
* Horário: 23h34
*/
classe pública SafeTask implementa Runnable {
private static ThreadLocal<Data> startDate = novo
ThreadLocal<Data>() {
@Substituir
Data protegida valorinicial() {
retornar nova Data();
}
};
@Substituir
execução void pública() {
System.out.printf("Tópico inicial: %s: %s/n",
Thread.currentThread().getId(), startDate.get());
tentar {
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Tópico finalizado: %s: %s/n",
Thread.currentThread().getId(), startDate.get());
}
}
Código completo da classe SafeMain:
Copie o código do código da seguinte forma:
pacote com.diguage.books.concurrencycookbook.chapter1.recipe9;
importar java.util.concurrent.TimeUnit;
/**
* Exemplo de thread seguro
* Data: 24/09/2013
* Horário: 00h04
*/
classe pública SafeMain {
public static void main(String[] args) {
Tarefa SafeTask = new SafeTask();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(tarefa);
thread.start();
tentar {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}