Alguém pode me dizer a vantagem do método sincronizado sobre o bloco sincronizado com um exemplo?
Alguém pode me dizer a vantagem do método sincronizado sobre o bloco sincronizado com um exemplo?
Respostas:
Alguém pode me dizer a vantagem do método sincronizado sobre o bloco sincronizado com um exemplo? Obrigado.
Não há uma clara vantagem de usar o método sincronizado sobre o bloco.
Talvez o único (mas eu não chamaria isso de vantagem) é que você não precisa incluir a referência do objeto this
.
Método:
public synchronized void method() { // blocks "this" from here....
...
...
...
} // to here
Quadra:
public void method() {
synchronized( this ) { // blocks "this" from here ....
....
....
....
} // to here...
}
Vejo? Nenhuma vantagem.
Blocos que têm vantagens sobre os métodos, porém, principalmente na flexibilidade porque você pode usar outro objeto como bloqueio enquanto sincronizar o método seria bloquear todo o objeto.
Comparar:
// locks the whole object
...
private synchronized void someInputRelatedWork() {
...
}
private synchronized void someOutputRelatedWork() {
...
}
vs.
// Using specific locks
Object inputLock = new Object();
Object outputLock = new Object();
private void someInputRelatedWork() {
synchronized(inputLock) {
...
}
}
private void someOutputRelatedWork() {
synchronized(outputLock) {
...
}
}
Além disso, se o método crescer, você ainda poderá manter a seção sincronizada separada:
private void method() {
... code here
... code here
... code here
synchronized( lock ) {
... very few lines of code here
}
... code here
... code here
... code here
... code here
}
synchronized
bloco é implementado usando duas instruções monitorenter
e monitorexit
, além de um manipulador de exceções que garante que isso monitorexit
seja chamado mesmo no caso excepcional. Tudo isso é salvo ao usar um synchronized
método.
A única diferença real é que um bloco sincronizado pode escolher em qual objeto ele será sincronizado. Um método sincronizado pode usar apenas 'this'
(ou a instância de Class correspondente para um método de classe sincronizado). Por exemplo, estes são semanticamente equivalentes:
synchronized void foo() {
...
}
void foo() {
synchronized (this) {
...
}
}
O último é mais flexível, pois pode competir pelo bloqueio associado de qualquer objeto, geralmente uma variável de membro. Também é mais granular porque você pode ter código simultâneo executando antes e depois do bloco, mas ainda dentro do método. Obviamente, você poderia facilmente usar um método sincronizado refatorando o código simultâneo em métodos não sincronizados separados. Use o que tornar o código mais compreensível.
Prós:
Contras:
Prós:
Contras:
Pessoalmente, prefiro usar métodos sincronizados com classes focadas apenas na coisa que precisa de sincronização. Essa classe deve ser a menor possível e, portanto, deve ser fácil revisar a sincronização. Outros não precisam se preocupar com a sincronização.
A principal diferença é que, se você usar um bloco sincronizado, poderá bloquear um objeto que não seja o que permite ser muito mais flexível.
Suponha que você tenha uma fila de mensagens e vários produtores e consumidores de mensagens. Não queremos que os produtores interfiram entre si, mas os consumidores devem poder recuperar mensagens sem ter que esperar pelos produtores. Então, nós apenas criamos um objeto
Object writeLock = new Object();
E a partir de agora, toda vez que um produtor quiser adicionar uma nova mensagem, apenas bloqueamos:
synchronized(writeLock){
// do something
}
Portanto, os consumidores ainda podem ler e os produtores serão bloqueados.
Método sincronizado
Métodos sincronizados têm dois efeitos.
Primeiro, quando um encadeamento está executando um método sincronizado para um objeto, todos os outros encadeamentos que invocam métodos sincronizados para o mesmo bloco de objetos (suspender a execução) até que o primeiro encadeamento seja concluído com o objeto.
Segundo, quando um método sincronizado sai, ele estabelece automaticamente um relacionamento de antes do acontecimento com qualquer chamada subsequente de um método sincronizado para o mesmo objeto. Isso garante que as alterações no estado do objeto sejam visíveis para todos os threads.
Observe que os construtores não podem ser sincronizados - o uso da palavra-chave sincronizada com um construtor é um erro de sintaxe. Sincronizar construtores não faz sentido, porque apenas o encadeamento que cria um objeto deve ter acesso a ele enquanto está sendo construído.
Instrução Sincronizada
Diferentemente dos métodos sincronizados, as instruções sincronizadas devem especificar o objeto que fornece o bloqueio intrínseco: Na maioria das vezes, eu uso isso para sincronizar o acesso a uma lista ou mapa, mas não quero bloquear o acesso a todos os métodos do objeto.
P: Bloqueios intrínsecos e sincronização A sincronização é criada em torno de uma entidade interna conhecida como bloqueio intrínseco ou bloqueio do monitor. (A especificação da API geralmente se refere a essa entidade simplesmente como um "monitor".) Os bloqueios intrínsecos desempenham um papel em ambos os aspectos da sincronização: impor acesso exclusivo ao estado de um objeto e estabelecer relacionamentos anteriores ao passado, essenciais para a visibilidade.
Todo objeto tem um bloqueio intrínseco associado a ele. Por convenção, um encadeamento que precisa de acesso exclusivo e consistente aos campos de um objeto precisa adquirir o bloqueio intrínseco do objeto antes de acessá-los e liberar o bloqueio intrínseco quando terminar com eles. Diz-se que uma linha possui a trava intrínseca entre o momento em que ela adquiriu a trava e a liberou. Desde que um encadeamento possua um bloqueio intrínseco, nenhum outro encadeamento poderá adquirir o mesmo bloqueio. O outro encadeamento será bloqueado quando tentar obter o bloqueio.
package test;
public class SynchTest implements Runnable {
private int c = 0;
public static void main(String[] args) {
new SynchTest().test();
}
public void test() {
// Create the object with the run() method
Runnable runnable = new SynchTest();
Runnable runnable2 = new SynchTest();
// Create the thread supplying it with the runnable object
Thread thread = new Thread(runnable,"thread-1");
Thread thread2 = new Thread(runnable,"thread-2");
// Here the key point is passing same object, if you pass runnable2 for thread2,
// then its not applicable for synchronization test and that wont give expected
// output Synchronization method means "it is not possible for two invocations
// of synchronized methods on the same object to interleave"
// Start the thread
thread.start();
thread2.start();
}
public synchronized void increment() {
System.out.println("Begin thread " + Thread.currentThread().getName());
System.out.println(this.hashCode() + "Value of C = " + c);
// If we uncomment this for synchronized block, then the result would be different
// synchronized(this) {
for (int i = 0; i < 9999999; i++) {
c += i;
}
// }
System.out.println("End thread " + Thread.currentThread().getName());
}
// public synchronized void decrement() {
// System.out.println("Decrement " + Thread.currentThread().getName());
// }
public int value() {
return c;
}
@Override
public void run() {
this.increment();
}
}
Verifique as saídas diferentes com o método sincronizado, em bloco e sem sincronização.
Nota: métodos e blocos estáticos sincronizados funcionam no objeto Class.
public class MyClass {
// locks MyClass.class
public static synchronized void foo() {
// do something
}
// similar
public static void foo() {
synchronized(MyClass.class) {
// do something
}
}
}
Quando o compilador java converte seu código-fonte em código de bytes, ele lida com métodos e blocos sincronizados de maneira muito diferente.
Quando a JVM executa um método sincronizado, o encadeamento em execução identifica que a estrutura method_info do método possui o sinalizador ACC_SYNCHRONIZED definido e adquire automaticamente o bloqueio do objeto, chama o método e libera o bloqueio. Se ocorrer uma exceção, o encadeamento libera automaticamente o bloqueio.
A sincronização de um bloco de método, por outro lado, ignora o suporte interno da JVM para adquirir o manuseio de bloqueio e exceção de um objeto e requer que a funcionalidade seja explicitamente escrita em código de bytes. Se você ler o código de bytes de um método com um bloco sincronizado, verá mais de uma dúzia de operações adicionais para gerenciar essa funcionalidade.
Isso mostra as chamadas para gerar um método sincronizado e um bloco sincronizado:
public class SynchronizationExample {
private int i;
public synchronized int synchronizedMethodGet() {
return i;
}
public int synchronizedBlockGet() {
synchronized( this ) {
return i;
}
}
}
O synchronizedMethodGet()
método gera o seguinte código de bytes:
0: aload_0
1: getfield
2: nop
3: iconst_m1
4: ireturn
E aqui está o código de bytes do synchronizedBlockGet()
método:
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: aload_0
5: getfield
6: nop
7: iconst_m1
8: aload_1
9: monitorexit
10: ireturn
11: astore_2
12: aload_1
13: monitorexit
14: aload_2
15: athrow
Uma diferença significativa entre o método sincronizado e o bloco é que, geralmente, o bloco sincronizado reduz o escopo do bloqueio. Como o escopo do bloqueio é inversamente proporcional ao desempenho, é sempre melhor bloquear apenas uma seção crítica do código. Um dos melhores exemplos de uso do bloco sincronizado é o bloqueio verificado duas vezes no padrão Singleton, onde, em vez de bloquear todo o getInstance()
método, apenas bloqueamos a seção crítica do código que é usada para criar a instância Singleton. Isso melhora drasticamente o desempenho, porque o bloqueio é necessário apenas uma ou duas vezes.
Ao usar métodos sincronizados, você precisará ter cuidado extra se misturar métodos sincronizados estáticos e não estáticos.
monitorenter
e monitorexit
antes de executar o código.
Na maioria das vezes eu uso isso para sincronizar o acesso a uma lista ou mapa, mas não quero bloquear o acesso a todos os métodos do objeto.
No código a seguir, um thread modificando a lista não bloqueará a espera de um thread que está modificando o mapa. Se os métodos foram sincronizados no objeto, cada método teria que esperar, mesmo que as modificações que estão fazendo não entrem em conflito.
private List<Foo> myList = new ArrayList<Foo>();
private Map<String,Bar) myMap = new HashMap<String,Bar>();
public void put( String s, Bar b ) {
synchronized( myMap ) {
myMap.put( s,b );
// then some thing that may take a while like a database access or RPC or notifying listeners
}
}
public void hasKey( String s, ) {
synchronized( myMap ) {
myMap.hasKey( s );
}
}
public void add( Foo f ) {
synchronized( myList ) {
myList.add( f );
// then some thing that may take a while like a database access or RPC or notifying listeners
}
}
public Thing getMedianFoo() {
Foo med = null;
synchronized( myList ) {
Collections.sort(myList);
med = myList.get(myList.size()/2);
}
return med;
}
Com blocos sincronizados, você pode ter vários sincronizadores, para que várias coisas simultâneas, mas não conflitantes, possam continuar ao mesmo tempo.
Métodos sincronizados podem ser verificados usando a API de reflexão. Isso pode ser útil para testar alguns contratos, como todos os métodos no modelo são sincronizados .
O seguinte fragmento imprime todos os métodos sincronizados do Hashtable:
for (Method m : Hashtable.class.getMethods()) {
if (Modifier.isSynchronized(m.getModifiers())) {
System.out.println(m);
}
}
Nota importante sobre o uso do bloco sincronizado: cuidado com o que você usa como objeto de bloqueio!
O trecho de código do usuário2277816 acima ilustra esse ponto em que uma referência a um literal de seqüência de caracteres é usada como objeto de bloqueio. Perceba que os literais de strings são automaticamente internados em Java e você deve começar a ver o problema: todo trecho de código que é sincronizado no "bloqueio" literal, compartilha o mesmo bloqueio! Isso pode facilmente levar a conflitos com trechos de código completamente não relacionados.
Não são apenas os objetos String que você precisa ter cuidado. As primitivas in a box também são um perigo, pois a caixa automática e os métodos valueOf podem reutilizar os mesmos objetos, dependendo do valor.
Para obter mais informações, consulte: https://www.securecoding.cert.org/confluence/display/java/LCK01-J.+Do+not+synchronize+on+objects+that+may+be+reused
Geralmente, o uso de uma trava no nível do método é muito rude. Por que bloquear um código que não acessa nenhum recurso compartilhado bloqueando um método inteiro. Como cada objeto possui um bloqueio, é possível criar objetos fictícios para implementar a sincronização no nível do bloco. O nível do bloco é mais eficiente porque não bloqueia todo o método.
Aqui algum exemplo
Nível do método
class MethodLevel {
//shared among threads
SharedResource x, y ;
public void synchronized method1() {
//multiple threads can't access
}
public void synchronized method2() {
//multiple threads can't access
}
public void method3() {
//not synchronized
//multiple threads can access
}
}
Nível do bloco
class BlockLevel {
//shared among threads
SharedResource x, y ;
//dummy objects for locking
Object xLock = new Object();
Object yLock = new Object();
public void method1() {
synchronized(xLock){
//access x here. thread safe
}
//do something here but don't use SharedResource x, y
// because will not be thread-safe
synchronized(xLock) {
synchronized(yLock) {
//access x,y here. thread safe
}
}
//do something here but don't use SharedResource x, y
//because will not be thread-safe
}//end of method1
}
[Editar]
Para Collection
gostar Vector
e Hashtable
eles são sincronizados quando ArrayList
ou HashMap
não, e você precisa definir a palavra-chave sincronizada ou invocar o método sincronizado de Coleções:
Map myMap = Collections.synchronizedMap (myMap); // single lock for the entire map
List myList = Collections.synchronizedList (myList); // single lock for the entire list
A única diferença: os blocos sincronizados permitem o bloqueio granular, diferentemente do método sincronizado
Basicamente synchronized
, métodos ou blocos foram usados para escrever código seguro de thread, evitando erros de inconsistência de memória.
Esta questão é muito antiga e muitas coisas foram alteradas nos últimos 7 anos. Novas construções de programação foram introduzidas para segurança do encadeamento.
Você pode obter segurança de encadeamento usando API de simultaneidade avançada em vez de synchronied
blocos. Esta página de documentação fornece boas construções de programação para garantir a segurança do encadeamento.
Os objetos de bloqueio oferecem suporte a idiomas de bloqueio que simplificam muitos aplicativos simultâneos.
Os executivos definem uma API de alto nível para iniciar e gerenciar threads. As implementações de executores fornecidas pelo java.util.concurrent fornecem gerenciamento de conjunto de encadeamentos adequado para aplicativos de grande escala.
Coleções simultâneas facilitam o gerenciamento de grandes coleções de dados e podem reduzir bastante a necessidade de sincronização.
As variáveis atômicas possuem recursos que minimizam a sincronização e ajudam a evitar erros de consistência de memória.
O ThreadLocalRandom (no JDK 7) fornece geração eficiente de números pseudo-aleatórios a partir de vários encadeamentos.
Melhor substituto para o sincronizado é o ReentrantLock , que usa Lock
API
Um bloqueio de exclusão mútua reentrante com o mesmo comportamento básico e semântica que o bloqueio implícito do monitor acessado usando métodos e instruções sincronizados, mas com recursos estendidos.
Exemplo com bloqueios:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
Consulte java.util.concurrent e java.util.concurrent.atomic pacotes também para outras construções de programação.
Consulte também esta questão relacionada:
Em geral, eles são basicamente os mesmos, exceto por serem explícitos sobre o monitor do objeto que está sendo usado versus o implícito desse objeto. Uma desvantagem dos métodos sincronizados que eu acho que às vezes é negligenciada é que, ao usar a referência "this" para sincronizar, você deixa em aberto a possibilidade de objetos externos travarem no mesmo objeto. Isso pode ser um bug muito sutil, se você o encontrar. A sincronização em um objeto explícito interno ou em outro campo existente pode evitar esse problema, encapsulando completamente a sincronização.
Como já foi dito aqui, o bloco sincronizado pode usar a variável definida pelo usuário como objeto de bloqueio, quando a função sincronizada usa apenas "this". E é claro que você pode manipular com áreas de sua função que devem ser sincronizadas. Mas todo mundo diz que não há diferença entre a função sincronizada e o bloco, que cobre toda a função usando "this" como objeto de bloqueio. Isso não é verdade, a diferença está no código de bytes que será gerado nas duas situações. No caso de uso sincronizado de blocos, deve ser alocada uma variável local que contém referência a "this". E, como resultado, teremos um tamanho um pouco maior para a função (não relevante se você tiver apenas um pequeno número de funções).
Explicação mais detalhada da diferença que você pode encontrar aqui: http://www.artima.com/insidejvm/ed2/threadsynchP.html
No caso de métodos sincronizados, o bloqueio será adquirido em um Objeto. Mas se você optar pelo bloco sincronizado, terá a opção de especificar um objeto no qual o bloqueio será adquirido.
Exemplo:
Class Example {
String test = "abc";
// lock will be acquired on String test object.
synchronized (test) {
// do something
}
lock will be acquired on Example Object
public synchronized void testMethod() {
// do some thing
}
}
Sei que essa é uma pergunta antiga, mas com a rápida leitura das respostas aqui, não vi ninguém mencionar que, às vezes, um synchronized
método pode ser o bloqueio errado .
Na Concorrência Java na prática (pág. 72):
public class ListHelper<E> {
public List<E> list = Collections.syncrhonizedList(new ArrayList<>());
...
public syncrhonized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
O código acima parece ser seguro para threads. No entanto, na realidade não é. Nesse caso, o bloqueio é obtido na instância da classe. No entanto, é possível que a lista seja modificada por outro encadeamento que não use esse método. A abordagem correta seria usar
public boolean putIfAbsent(E x) {
synchronized(list) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
}
O código acima impediria que todos os threads que tentassem modificar a lista modificassem a lista até que o bloco sincronizado fosse concluído.
List
pode levar a problemas de desempenho se houver um registro de código que não necessariamente precisam ser sincronizados
Por uma questão prática, a vantagem dos métodos sincronizados sobre os blocos sincronizados é que eles são mais resistentes a idiotas; porque você não pode escolher um objeto arbitrário para bloquear, não pode usar indevidamente a sintaxe do método sincronizado para fazer coisas estúpidas, como bloquear uma literal de string ou bloquear o conteúdo de um campo mutável que é alterado sob os threads.
Por outro lado, com métodos sincronizados, você não pode impedir que o bloqueio seja adquirido por qualquer thread que possa obter uma referência ao objeto.
Portanto, usar a sincronização como um modificador nos métodos é melhor para proteger seus cow-orkers de se machucarem, enquanto o uso de blocos sincronizados em conjunto com objetos de bloqueio final privados é melhor para proteger seu próprio código dos cow-orkers.
Em um resumo de especificação Java: http://www.cs.cornell.edu/andru/javaspec/17.doc.html
A declaração sincronizada (§14.17) calcula uma referência a um objeto; ele tenta executar uma ação de bloqueio nesse objeto e não prossegue até que a ação de bloqueio seja concluída com êxito. ...
Um método sincronizado (§8.4.3.5) executa automaticamente uma ação de bloqueio quando é invocada; seu corpo não é executado até que a ação de bloqueio seja concluída com êxito. Se o método for um método de instância , ele bloqueia o bloqueio associado à instância para a qual foi invocado (ou seja, o objeto que será conhecido como esse durante a execução do corpo do método). Se o método for estático , ele bloqueia o bloqueio associado ao objeto Class que representa a classe na qual o método está definido. ...
Com base nessas descrições, eu diria que a maioria das respostas anteriores está correta, e um método sincronizado pode ser particularmente útil para métodos estáticos, onde você teria que descobrir como obter o "objeto de classe que representa a classe na qual o método estava" definiram."
Edit: Eu originalmente pensei que estas eram citações da especificação Java real. Esclareceu que esta página é apenas um resumo / explicação das especificações
TLDR; Não use o synchronized
modificador nem a synchronized(this){...}
expressão, mas synchronized(myLock){...}
onde myLock
existe um campo de instância final contendo um objeto particular.
A diferença entre o uso do synchronized
modificador na declaração do método e a synchronized(..){ }
expressão no corpo do método é a seguinte:
synchronized
modificador especificado na assinatura do método
synchronized(this) { .... }
, ethis
objeto como bloqueio quando declarado em método não estático ou a classe envolvente quando declarado em método estático.synchronized(...){...}
expressão permite que você
No entanto, o uso do synchronized
modificador ou synchronized(...) {...}
com this
o objeto de bloqueio (como em synchronized(this) {...}
) tem a mesma desvantagem. Ambos usam sua própria instância como o objeto de bloqueio para sincronização. Isso é perigoso porque não apenas o objeto em si, mas qualquer outro objeto / código externo que contenha uma referência a esse objeto também pode usá-lo como um bloqueio de sincronização com efeitos colaterais potencialmente graves (degradação do desempenho e conflitos ).
Portanto, a melhor prática é não usar o synchronized
modificador nem a synchronized(...)
expressão em conjunto com this
como objeto de bloqueio, mas um objeto de bloqueio privado para esse objeto. Por exemplo:
public class MyService {
private final lock = new Object();
public void doThis() {
synchronized(lock) {
// do code that requires synchronous execution
}
}
public void doThat() {
synchronized(lock) {
// do code that requires synchronous execution
}
}
}
Você também pode usar vários objetos de bloqueio, mas é necessário tomar cuidado especial para garantir que isso não resulte em conflitos quando usados aninhados.
public class MyService {
private final lock1 = new Object();
private final lock2 = new Object();
public void doThis() {
synchronized(lock1) {
synchronized(lock2) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThat() and doMore().
}
}
public void doThat() {
synchronized(lock1) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThis().
// doMore() may execute concurrently
}
}
public void doMore() {
synchronized(lock2) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThis().
// doThat() may execute concurrently
}
}
}
Suponho que esta pergunta seja sobre a diferença entre o Thread Safe Singleton e a inicialização Lazy com bloqueio de verificação dupla . Sempre me refiro a este artigo quando preciso implementar algum singleton específico.
Bem, este é um Singleton seguro para threads :
// Java program to create Thread Safe
// Singleton class
public class GFG
{
// private instance, so that it can be
// accessed by only by getInstance() method
private static GFG instance;
private GFG()
{
// private constructor
}
//synchronized method to control simultaneous access
synchronized public static GFG getInstance()
{
if (instance == null)
{
// if instance is null, initialize
instance = new GFG();
}
return instance;
}
}
Prós:
Inicialização lenta é possível.
É thread-safe.
Contras:
- O método getInstance () é sincronizado, causando um desempenho lento, pois vários threads não podem acessá-lo simultaneamente.
Esta é uma inicialização lenta com bloqueio de verificação dupla :
// Java code to explain double check locking
public class GFG
{
// private instance, so that it can be
// accessed by only by getInstance() method
private static GFG instance;
private GFG()
{
// private constructor
}
public static GFG getInstance()
{
if (instance == null)
{
//synchronized block to remove overhead
synchronized (GFG.class)
{
if(instance==null)
{
// if instance is null, initialize
instance = new GFG();
}
}
}
return instance;
}
}
Prós:
Inicialização lenta é possível.
Também é thread-safe.
O desempenho reduzido por causa da palavra-chave sincronizada é superado.
Contras:
Primeira vez, isso pode afetar o desempenho.
Como contras. O método de bloqueio de verificação dupla é suportável, portanto pode ser usado para aplicativos multithread de alto desempenho.
Consulte este artigo para obter mais detalhes:
https://www.geeksforgeeks.org/java-singleton-design-pattern-practices-examples/
Sincronizando com threads. 1) NUNCA use sincronizado (isto) em um thread que não funcione. A sincronização com (this) usa o encadeamento atual como o objeto de encadeamento. Como cada segmento é independente de outros segmentos, NÃO há coordenação de sincronização. 2) Testes de código mostram que no Java 1.6 em um Mac a sincronização do método não funciona. 3) sincronizado (lockObj), em que lockObj é um objeto compartilhado comum de todos os threads sincronizados nele. 4) ReenterantLock.lock () e .unlock () funcionam. Veja os tutoriais de Java para isso.
O código a seguir mostra esses pontos. Ele também contém o vetor de thread-safe que seria substituído pelo ArrayList, para mostrar que muitos threads adicionados a um Vector não perdem nenhuma informação, enquanto o mesmo com um ArrayList pode perder informações. 0) O código atual mostra a perda de informações devido às condições da corrida. A) Comente a linha A atualmente rotulada e remova o comentário da linha A acima dela, depois execute, o método perde dados, mas não deve. B) Inverta a etapa A, remova o comentário B e // finalize o bloco}. Em seguida, execute para ver os resultados sem perda de dados. C) Comente B, descomente C. Execute, veja a sincronização em (isto) perde dados, conforme o esperado. Não tenha tempo para concluir todas as variações, espero que ajude. Se a sincronização estiver ativada (isto) ou a sincronização do método funcionar, indique qual versão do Java e do SO você testou. Obrigado.
import java.util.*;
/** RaceCondition - Shows that when multiple threads compete for resources
thread one may grab the resource expecting to update a particular
area but is removed from the CPU before finishing. Thread one still
points to that resource. Then thread two grabs that resource and
completes the update. Then thread one gets to complete the update,
which over writes thread two's work.
DEMO: 1) Run as is - see missing counts from race condition, Run severa times, values change
2) Uncomment "synchronized(countLock){ }" - see counts work
Synchronized creates a lock on that block of code, no other threads can
execute code within a block that another thread has a lock.
3) Comment ArrayList, unComment Vector - See no loss in collection
Vectors work like ArrayList, but Vectors are "Thread Safe"
May use this code as long as attribution to the author remains intact.
/mf
*/
public class RaceCondition {
private ArrayList<Integer> raceList = new ArrayList<Integer>(); // simple add(#)
// private Vector<Integer> raceList = new Vector<Integer>(); // simple add(#)
private String countLock="lock"; // Object use for locking the raceCount
private int raceCount = 0; // simple add 1 to this counter
private int MAX = 10000; // Do this 10,000 times
private int NUM_THREADS = 100; // Create 100 threads
public static void main(String [] args) {
new RaceCondition();
}
public RaceCondition() {
ArrayList<Thread> arT = new ArrayList<Thread>();
// Create thread objects, add them to an array list
for( int i=0; i<NUM_THREADS; i++){
Thread rt = new RaceThread( ); // i );
arT.add( rt );
}
// Start all object at once.
for( Thread rt : arT ){
rt.start();
}
// Wait for all threads to finish before we can print totals created by threads
for( int i=0; i<NUM_THREADS; i++){
try { arT.get(i).join(); }
catch( InterruptedException ie ) { System.out.println("Interrupted thread "+i); }
}
// All threads finished, print the summary information.
// (Try to print this informaiton without the join loop above)
System.out.printf("\nRace condition, should have %,d. Really have %,d in array, and count of %,d.\n",
MAX*NUM_THREADS, raceList.size(), raceCount );
System.out.printf("Array lost %,d. Count lost %,d\n",
MAX*NUM_THREADS-raceList.size(), MAX*NUM_THREADS-raceCount );
} // end RaceCondition constructor
class RaceThread extends Thread {
public void run() {
for ( int i=0; i<MAX; i++){
try {
update( i );
} // These catches show when one thread steps on another's values
catch( ArrayIndexOutOfBoundsException ai ){ System.out.print("A"); }
catch( OutOfMemoryError oome ) { System.out.print("O"); }
}
}
// so we don't lose counts, need to synchronize on some object, not primitive
// Created "countLock" to show how this can work.
// Comment out the synchronized and ending {, see that we lose counts.
// public synchronized void update(int i){ // use A
public void update(int i){ // remove this when adding A
// synchronized(countLock){ // or B
// synchronized(this){ // or C
raceCount = raceCount + 1;
raceList.add( i ); // use Vector
// } // end block for B or C
} // end update
} // end RaceThread inner class
} // end RaceCondition outter class