Qual è il problema di concorrenza più frequente che hai riscontrato in Java? [chiuso]


192

Questo è un sondaggio sui problemi di concorrenza comuni in Java. Un esempio potrebbe essere il classico deadlock o la condizione di gara o forse i bug di threading EDT in Swing. Sono interessato sia a una vasta gamma di possibili problemi, sia a quelli più comuni. Quindi, si prega di lasciare una risposta specifica di un bug di concorrenza Java per commento e votare se ne vedi uno che hai riscontrato.


16
Perché è chiuso? Ciò è utile sia per gli altri programmatori che chiedono concorrenza in Java, sia per avere un'idea di quali classi di difetti di concorrenza vengono osservate di più dagli altri sviluppatori Java.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

@Longpoke Il messaggio di chiusura spiega perché è chiuso. Questa non è una domanda con una risposta "corretta" specifica, è più una domanda di sondaggio / elenco. Stack Overflow non intende ospitare questo tipo di domande. Se non sei d'accordo con questa politica, potresti discuterne su meta .
Andrzej Doyle,

7
Immagino che la community non sia d'accordo poiché questo articolo sta ottenendo oltre 100 visualizzazioni / giorno! L'ho trovato molto utile in quanto sono coinvolto nello sviluppo di uno strumento di analisi statica appositamente progettato per risolvere i problemi di concorrenza contemplateltd.com/threadsafe . Avere una banca di problemi di concorrenza comunemente riscontrati è stato ottimo per testare e migliorare ThreadSafe.
Craig Manson

Elenco di controllo per la revisione del codice per Java Concurrency digerisce la maggior parte delle insidie ​​menzionate nelle risposte a questa domanda in una forma conveniente per le revisioni quotidiane del codice.
leventov,

Risposte:


125

Il problema di concorrenza più comune che ho visto non è rendersi conto che un campo scritto da un thread non è garantito per essere visto da un thread diverso. Un'applicazione comune di questo:

class MyThread extends Thread {
  private boolean stop = false;

  public void run() {
    while(!stop) {
      doSomeWork();
    }
  }

  public void setStop() {
    this.stop = true;
  }
}

Finché stop non è volatile, o setStope runnon sono sincronizzati questo non è garantito il funzionamento. Questo errore è particolarmente diabolico in quanto nel 99,999% non importerà in pratica poiché il thread del lettore alla fine vedrà il cambiamento, ma non sappiamo quanto presto lo abbia visto.


9
Un'ottima soluzione a questo è quella di rendere AtomicBoolean la variabile di istanza stop. Risolve tutti i problemi dei non volatili, proteggendoti dai problemi di JMM.
Kirk Wylie,

39
È peggio di "per diversi minuti" - potresti MAI vederlo. In base al modello di memoria, la JVM è autorizzata a ottimizzare while (! Stop) in while (true) e quindi viene eseguito il hosing. Questo può accadere solo su alcune VM, solo in modalità server, solo quando la JVM si ricompila dopo x iterazioni del loop, ecc. Ahi!
Cowan,

2
Perché dovresti usare AtomicBoolean su un valore booleano volatile? Sto sviluppando per la versione 1.4+, quindi ci sono insidie ​​nel dichiarare volatile?
Pool

2
Nick, penso sia perché il CAS atomico di solito è persino più veloce della volatile. Se stai sviluppando per 1.4 l'unica opzione sicura che IMHO è usare sincronizzato come volatile in 1.4 non ha le forti garanzie di barriera di memoria come in Java 5.
Kutzi

5
@Thomas: questo è dovuto al modello di memoria Java. Dovresti leggerlo, se vuoi conoscerlo in dettaglio (Java Concurrency in Practice di Brian Goetz lo spiega bene, ad esempio). In breve: a meno che tu non usi parole chiave / costrutti per la sincronizzazione della memoria (come volatile, sincronizzato, AtomicXyz, ma anche quando un thread è finito) un thread non ha alcuna garanzia di vedere le modifiche apportate a qualsiasi campo fatte da un thread diverso
Kutzi

179

Il mio problema di concorrenza più doloroso n. 1 si è mai verificato quando due diverse librerie open source hanno fatto qualcosa del genere:

private static final String LOCK = "LOCK";  // use matching strings 
                                            // in two different libraries

public doSomestuff() {
   synchronized(LOCK) {
       this.work();
   }
}

A prima vista, questo sembra un esempio di sincronizzazione piuttosto banale. Però; poiché le stringhe sono internate in Java, la stringa letterale "LOCK"risulta essere la stessa istanza di java.lang.String(anche se sono dichiarate in modo completamente disparato l'una dall'altra). Il risultato è ovviamente negativo.


63
Questo è uno dei motivi per cui preferisco Object LOCK = new Object ();
Andrzej Doyle,

17
Lo adoro - oh, è brutto :)
Thorbjørn Ravn Andersen,

7
È una buona idea per Java Puzzlers 2.
Dov Wasserman,

12
In realtà ... mi fa davvero desiderare che il compilatore si rifiuti di permetterti di sincronizzarti su una stringa. Dato lo string interning, non vi è alcun caso in cui sarebbe una "cosa buona (tm)".
Jared

3
@Jared: "fino a quando la stringa non viene internata" non ha senso. Le stringhe non "magicamente" vengono internate. String.intern () restituisce un oggetto diverso, a meno che tu non abbia già l'istanza canonica della stringa specificata. Inoltre, tutte le stringhe letterali e le espressioni costanti con valore di stringa vengono internate. Sempre. Vedi i documenti per String.intern () e §3.10.5 di JLS.
Laurence Gonsalves il

65

Un problema classico è la modifica dell'oggetto su cui si sta eseguendo la sincronizzazione durante la sincronizzazione:

synchronized(foo) {
  foo = ...
}

Altri thread simultanei si stanno quindi sincronizzando su un oggetto diverso e questo blocco non fornisce l'esclusione reciproca che ci si aspetta.


19
Esiste un'ispezione IDEA per questo chiamata "Sincronizzazione sul campo non finale che probabilmente avrà una semantica utile". Molto bella.
Jen S.

8
Ah ... ora questa è una descrizione torturata. "è improbabile che abbia una semantica utile" potrebbe essere meglio descritto come "molto probabilmente rotto". :)
Alex Miller,

Penso che sia stato Bitter Java ad avere questo nel suo ReadWriteLock. Fortunatamente ora abbiamo java.util.concurrency.locks e Doug è un po 'più in gamba.
Tom Hawtin - tackline

Ho anche visto questo problema spesso. Sincronizza solo sugli oggetti finali, del resto. FindBugs et al. aiuto, si.
gimpf,

è solo un problema durante l'incarico? (vedi l'esempio di @Alex Miller di seguito con una mappa) Anche quell'esempio di mappa avrebbe lo stesso problema?
Alex Beardsley,

50

Un problema comune è l'utilizzo di classi come Calendar e SimpleDateFormat da più thread (spesso memorizzandole nella cache in una variabile statica) senza sincronizzazione. Queste classi non sono thread-safe, quindi l'accesso multi-thread alla fine causerà strani problemi con uno stato incoerente.


Conosci qualche progetto open source contenente questo bug in una versione di esso? Sto cercando esempi concreti di questo bug nel software del mondo reale.
riprogrammatore

48

Sincronizzazione non corretta sugli oggetti restituiti da Collections.synchronizedXXX(), in particolare durante iterazione o operazioni multiple:

Map<String, String> map = Collections.synchronizedMap(new HashMap<String, String>());

...

if(!map.containsKey("foo"))
    map.put("foo", "bar");

Questo è sbagliato . Nonostante siano singole operazioni synchronized, lo stato della mappa tra invocazione containse putpuò essere modificato da un altro thread. Dovrebbe essere:

synchronized(map) {
    if(!map.containsKey("foo"))
        map.put("foo", "bar");
}

O con ConcurrentMapun'implementazione:

map.putIfAbsent("foo", "bar");

6
O meglio, usa una ConcurrentHashMap e putIfAbsent.
Tom Hawtin - tackline

47

Blocco a doppio controllo. Nell'insieme.

Il paradigma, di cui ho iniziato a imparare i problemi di quando lavoravo in BEA, è che le persone controlleranno un singleton nel modo seguente:

public Class MySingleton {
  private static MySingleton s_instance;
  public static MySingleton getInstance() {
    if(s_instance == null) {
      synchronized(MySingleton.class) { s_instance = new MySingleton(); }
    }
    return s_instance;
  }
}

Questo non funziona mai, perché un altro thread potrebbe essere entrato nel blocco sincronizzato e s_instance non è più nullo. Quindi il cambiamento naturale è quindi quello di farlo:

  public static MySingleton getInstance() {
    if(s_instance == null) {
      synchronized(MySingleton.class) {
        if(s_instance == null) s_instance = new MySingleton();
      }
    }
    return s_instance;
  }

Neanche questo funziona, perché il modello di memoria Java non lo supporta. È necessario dichiarare s_instance come volatile per farlo funzionare e anche in questo caso funziona solo su Java 5.

Le persone che non hanno familiarità con le complessità del modello di memoria Java fanno casino tutto il tempo .


7
Il modello enum singleton risolve tutti questi problemi (vedere i commenti di Josh Bloch su questo). La conoscenza della sua esistenza dovrebbe essere più diffusa tra i programmatori Java.
Robin,

Devo ancora imbattermi in un singolo caso in cui l'inizializzazione lenta di un singleton era effettivamente appropriata. E se lo è, dichiara semplicemente il metodo sincronizzato.
Dov Wasserman,

3
Questo è quello che uso per l'inizializzazione Lazy delle classi Singleton. Inoltre, non è richiesta alcuna sincronizzazione poiché questo è implicitamente garantito da Java. class Foo {Titolare di classe static {statico Foo foo = new Foo (); } static Foo getInstance () {return Holder.foo; }}
Irfan Zulfiqar,

Irfan, si chiama metodo di Pugh, da quello che ricordo
Chris R,

@Robin, non è più semplice usare un inizializzatore statico? Questi sono sempre garantiti per l'esecuzione sincronizzata.
matt b

37

Sebbene probabilmente non sia esattamente quello che stai chiedendo, il problema più frequente relativo alla concorrenza che ho riscontrato (probabilmente perché si presenta nel normale codice a thread singolo) è un

java.util.ConcurrentModificationException

causato da cose come:

List<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c"));
for (String string : list) { list.remove(string); }

No, è proprio quello che sto cercando. Grazie!
Alex Miller,

30

Può essere facile pensare che le raccolte sincronizzate ti garantiscano una protezione maggiore di quanto non facciano effettivamente e dimenticare di tenere il blocco tra le chiamate. Ho visto questo errore alcune volte:

 List<String> l = Collections.synchronizedList(new ArrayList<String>());
 String[] s = l.toArray(new String[l.size()]);

Ad esempio, nella seconda riga sopra, i metodi toArray()e size()sono entrambi sicuri per i thread a sé stanti, ma size()vengono valutati separatamente da toArray()e il blocco sull'elenco non viene trattenuto tra queste due chiamate.

Se esegui questo codice con un altro thread rimuovendo contemporaneamente gli elementi dall'elenco, prima o poi finirai con un nuovo String[]reso che è più grande del necessario per contenere tutti gli elementi nell'elenco e ha valori nulli nella coda. È facile pensare che, poiché le due chiamate di metodo all'elenco avvengono in una singola riga di codice, si tratta in qualche modo di un'operazione atomica, ma non lo è.


5
buon esempio. Penso che lo scriverei più in generale perché "la composizione delle operazioni atomiche non è atomica". (Vedi campo volatile ++ per un altro semplice esempio)
Alex Miller,

29

Il bug più comune che vediamo dove lavoro è che i programmatori eseguono operazioni lunghe, come le chiamate del server, sull'EDT, bloccando la GUI per alcuni secondi e rendendo l'app non rispondente.


una di quelle risposte per le quali vorrei poter dare più di un punto
Epaga,

2
EDT = Thread di
invio

28

Dimenticare di aspettare () (o Condition.await ()) in un ciclo, verificando che la condizione di attesa sia effettivamente vera. Senza questo, ti imbatti in bug da risvegli spuri di wait (). L'uso canonico dovrebbe essere:

 synchronized (obj) {
     while (<condition does not hold>) {
         obj.wait();
     }
     // do stuff based on condition being true
 }

26

Un altro bug comune è la scarsa gestione delle eccezioni. Quando un thread in background genera un'eccezione, se non lo gestisci correttamente, potresti non vedere affatto la traccia dello stack. O forse l'attività in background si interrompe e non si riavvia mai perché non è stata gestita l'eccezione.


Sì, e ci sono buoni strumenti per gestirlo ora con i gestori.
Alex Miller,

3
Potresti pubblicare collegamenti ad articoli o riferimenti che spiegano questo in modo più dettagliato?
Abhijeet Kashnia,

22

Fino a quando non ho preso una lezione con Brian Goetz non mi sono reso conto che il non sincronizzato getterdi un campo privato mutato attraverso un sincronizzato nonsetter è mai garantito per restituire il valore aggiornato. Solo quando una variabile è protetta da un blocco sincronizzato su entrambe le letture E le scritture otterrai la garanzia dell'ultimo valore della variabile.

public class SomeClass{
    private Integer thing = 1;

    public synchronized void setThing(Integer thing)
        this.thing = thing;
    }

    /**
     * This may return 1 forever and ever no matter what is set
     * because the read is not synched
     */
    public Integer getThing(){
        return thing;  
    }
}

5
Nelle successive JVM (1.5 e successive, penso), l'uso di volatile risolverà anche questo.
James Schek,

2
Non necessariamente. volatile ti dà il valore più recente in modo da impedire la restituzione di 1 per sempre, ma non fornisce il blocco. È vicino, ma non è lo stesso.
John Russell,

1
@JohnRussell Pensavo che la volatilità garantisse una relazione prima. non è questo "blocco"? "Una scrittura su una variabile volatile (§8.3.1.4) v si sincronizza, con tutte le successive letture di v di qualsiasi thread (dove successivo è definito in base all'ordine di sincronizzazione)."
Shawn,

15

Pensando di scrivere codice a thread singolo, ma utilizzando statiche mutabili (inclusi i singleton). Ovviamente saranno condivisi tra i thread. Questo succede sorprendentemente spesso.


3
si Certamente. La statica mutabile rompe il confinamento del thread. Sorprendentemente, non ho mai trovato nulla su questa trappola in JCiP o CPJ.
Julien Chastang,

Spero che questo sia ovvio per la gente che sta programmando contemporaneamente. Lo stato globale dovrebbe essere il primo posto per verificare la sicurezza dei thread.
gtrak,

1
@Gary Thing è che non stanno pensando di fare una programmazione simultanea.
Tom Hawtin: affronta il

15

Le chiamate arbitrarie al metodo non devono essere effettuate all'interno di blocchi sincronizzati.

Dave Ray ha toccato questo aspetto nella sua prima risposta, e in effetti ho anche riscontrato un deadlock che ha a che fare con l'invocazione di metodi sugli ascoltatori all'interno di un metodo sincronizzato. Penso che la lezione più generale sia che le chiamate di metodo non dovrebbero essere "immerse nella natura" all'interno di un blocco sincronizzato - non hai idea se la chiamata sarà di lunga durata, causerà deadlock o altro.

In questo caso, e di solito in generale, la soluzione era ridurre l'ambito del blocco sincronizzato per proteggere solo una sezione privata di codice critica .

Inoltre, poiché ora stavamo accedendo alla raccolta di ascoltatori al di fuori di un blocco sincronizzato, l'abbiamo modificata in una raccolta copia su scrittura. O avremmo potuto semplicemente fare una copia difensiva della Collezione. Il punto è che di solito ci sono alternative per accedere in sicurezza a una raccolta di oggetti sconosciuti.


13

L'errore più recente relativo alla concorrenza in cui mi sono imbattuto era un oggetto che nel suo costruttore ha creato un ExecutorService, ma quando l'oggetto non è più stato referenziato, non ha mai arrestato ExecutorService. Pertanto, per un periodo di settimane, sono trapelate migliaia di thread, causando un arresto anomalo del sistema. (Tecnicamente, non si è arrestato in modo anomalo, ma ha smesso di funzionare correttamente, pur continuando a funzionare.)

Tecnicamente, suppongo che questo non sia un problema di concorrenza, ma è un problema relativo all'uso delle librerie java.util.concurrency.


11

La sincronizzazione sbilanciata, in particolare contro Maps, sembra essere un problema abbastanza comune. Molte persone credono che la sincronizzazione su faccia su una mappa (non una ConcurrentMap, ma dicono una HashMap) e non la sincronizzazione su ottiene è sufficiente. Questo, tuttavia, può portare a un ciclo infinito durante il re-hash.

Lo stesso problema (sincronizzazione parziale) può tuttavia verificarsi ovunque si abbia uno stato condiviso con letture e scritture.


11

Ho riscontrato un problema di concorrenza con i servlet, quando ci sono campi mutabili che verranno impostati da ogni richiesta. Ma c'è solo un'istanza servlet per tutte le richieste, quindi ha funzionato perfettamente in un singolo ambiente utente ma quando più di un utente ha richiesto il servlet si sono verificati risultati imprevedibili.

public class MyServlet implements Servlet{
    private Object something;

    public void service(ServletRequest request, ServletResponse response)
        throws ServletException, IOException{
        this.something = request.getAttribute("something");
        doSomething();
    }

    private void doSomething(){
        this.something ...
    }
}

10

Non esattamente un bug, ma il peccato peggiore è fornire una libreria che intendi usare ad altre persone, ma non dichiarare quali classi / metodi sono thread-safe e quali devono essere chiamati solo da un singolo thread ecc.

Sempre più persone dovrebbero utilizzare le annotazioni sulla concorrenza (ad es. @ThreadSafe, @GuardedBy ecc.) Descritte nel libro di Goetz.


9

Il mio problema più grande sono sempre stati i deadlock, specialmente causati dagli ascoltatori che sono stati licenziati con un lucchetto bloccato. In questi casi, è davvero facile ottenere il blocco invertito tra due thread. Nel mio caso, tra una simulazione in esecuzione in un thread e una visualizzazione della simulazione in esecuzione nel thread dell'interfaccia utente.

EDIT: spostato seconda parte per la risposta separata.


Puoi dividere l'ultimo in una risposta separata? Teniamolo 1 per post. Questi sono due davvero buoni.
Alex Miller,

9

L'avvio di un thread all'interno del costruttore di una classe è problematico. Se la classe viene estesa, il thread può essere avviato prima dell'esecuzione del costruttore della sottoclasse .


8

Classi mutabili in strutture di dati condivise

Thread1:
    Person p = new Person("John");
    sharedMap.put("Key", p);
    assert(p.getName().equals("John");  // sometimes passes, sometimes fails

Thread2:
    Person p = sharedMap.get("Key");
    p.setName("Alfonso");

Quando ciò accade, il codice è molto più complesso di questo esempio semplificato. Replicare, trovare e correggere il bug è difficile. Forse potrebbe essere evitato se potessimo contrassegnare determinate classi come immutabili e alcune strutture dati come solo oggetti immutabili.


8

La sincronizzazione su una stringa letterale o costante definita da una stringa letterale è (potenzialmente) un problema poiché la stringa letterale è internata e sarà condivisa da chiunque altro nella JVM usando la stessa stringa letterale. So che questo problema si è verificato nei server delle applicazioni e in altri scenari "container".

Esempio:

private static final String SOMETHING = "foo";

synchronized(SOMETHING) {
   //
}

In questo caso, chiunque utilizzi la stringa "pippo" per bloccare condivide lo stesso blocco.


Potenzialmente è bloccato. Il problema è che la semantica su QUANDO le stringhe sono internate non è definita (o, IMNSHO, non è definita). Una costante del tempo di compilazione di "pippo" viene internata, il "pippo" proveniente da un'interfaccia di rete viene internato solo se lo fai.
Kirk Wylie il

Bene, ecco perché ho usato specificamente una costante di stringa letterale, che è garantita per essere internata.
Alex Miller,

8

Credo che in futuro il problema principale con Java sarà la (mancanza di) garanzie di visibilità per i costruttori. Ad esempio, se si crea la seguente classe

class MyClass {
    public int a = 1;
}

e quindi basta leggere la proprietà di MyClass a da un altro thread, MyClass.a potrebbe essere 0 o 1, a seconda dell'implementazione e dell'umore di JavaVM. Oggi le probabilità che 'a' sia 1 sono molto alte. Ma sulle future macchine NUMA questo potrebbe essere diverso. Molte persone non ne sono consapevoli e credono che non debbano preoccuparsi del multi-threading durante la fase di inizializzazione.


Lo trovo leggermente sorprendente, ma so che sei un tipo intelligente Tim, quindi lo prenderò senza riferimento. :) Tuttavia, se un fosse definitivo, questo non sarebbe un problema, giusto? Saresti quindi vincolato dalla semantica del congelamento finale durante la costruzione?
Alex Miller,

Trovo ancora cose nel JMM che mi sorprendono, quindi non mi fiderei di me, ma ne sono abbastanza sicuro. Vedi anche cs.umd.edu/~pugh/java/memoryModel/… . Se il campo fosse definitivo non sarebbe un problema, sarebbe visibile dopo la fase di inizializzazione.
Tim Jansen,

2
Questo è solo un problema, se il riferimento dell'istanza appena creata è in uso già prima che il costruttore sia tornato / finito. Ad esempio, la classe si registra durante la costruzione in un pool pubblico e altri thread iniziano ad accedervi.
ReneS,

3
MyClass.a indica l'accesso statico e 'a' non è un membro statico di MyClass. A parte questo, è come afferma "ReneS", questo è solo un problema se si perde un riferimento all'oggetto non completato, come ad esempio l'aggiunta di "this" a qualche mappa esterna nel costruttore.
Markus Jevring il

7

L'errore più stupido che faccio spesso è dimenticare di sincronizzare prima di chiamare un avviso () o wait () su un oggetto.


8
A differenza della maggior parte dei problemi di concorrenza, non è facile da trovare? Almeno ottieni qui IllegalMonitorStateException ...
Programmatore fuorilegge il

Per fortuna è molto facile da trovare ... ma è ancora un errore stupido che mi fa perdere più tempo del dovuto :)
Dave Ray,

7

Utilizzo di un "nuovo oggetto ()" locale come mutex.

synchronized (new Object())
{
    System.out.println("sdfs");
}

Questo è inutile.


2
Questo è probabilmente inutile, ma l'atto della sincronizzazione fa alcune cose interessanti ... Certamente creare un nuovo oggetto ogni volta è uno spreco completo.
ALBERO

4
Non è inutile. È una barriera di memoria senza blocco.
David Roussel,

1
@David: l'unico problema - jvm potrebbe ottimizzarlo rimuovendo del tutto tale blocco
ancoraanothercoder

@insighter Vedo che la tua opinione è condivisa ibm.com/developerworks/java/library/j-jtp10185/index.html Sono d'accordo che è una cosa sciocca da fare, poiché non sai quando la barriera della memoria verrà sincronizzata, I stava solo sottolineando che sta facendo di più che niente.
David Roussel,

7

Un altro problema comune di "concorrenza" è l'uso del codice sincronizzato quando non è affatto necessario. Ad esempio, vedo ancora i programmatori che usano StringBuffero anche java.util.Vector(come variabili locali del metodo).


1
Questo non è un problema, ma non è necessario, perché dice alla JVM di sincronizzare i dati con la memoria globale e quindi potrebbe funzionare male su multi-cpus anche così, nessuno usa il blocco di sincronizzazione in modo simultaneo.
ReneS,

6

Più oggetti protetti da blocco ma a cui si accede comunemente in successione. Abbiamo incontrato un paio di casi in cui i blocchi sono ottenuti da codice diverso in diversi ordini, con conseguente deadlock.


5

Non rendersi conto che thisin una classe interna non appartiene thisla classe esterna. In genere in una classe interna anonima che implementa Runnable. Il problema alla radice è che poiché la sincronizzazione fa parte di tutti i Objects effettivamente non esiste alcun controllo statico del tipo. L'ho visto almeno due volte su usenet e appare anche nella concorrenza Java in pratica di Brian Goetz'z.

Le chiusure BGGA non ne soffrono perché non esiste alcuna thischiusura (fa thisriferimento alla classe esterna). Se usi non thisoggetti come blocchi, aggira questo problema e altri.


3

Uso di un oggetto globale come una variabile statica per il blocco.

Ciò porta a prestazioni pessime a causa della contesa.


Bene, a volte, a volte no. Se fosse così facile ...
gimpf,

Supponendo che il threading aiuti a migliorare le prestazioni per il problema dato, ne peggiora sempre le prestazioni non appena più di un thread accede al codice protetto dal blocco.
Kohlerm,

3

Honesly? Prima dell'avvento java.util.concurrent, il problema più comune in cui mi imbattevo abitualmente era quello che io chiamo "thread-thrashing": applicazioni che usano thread per la concorrenza, ma ne generano troppi e finiscono con il thrashing.


Stai implicando di avere più problemi ora che java.util.concurrent è disponibile?
Andrzej Doyle,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.