2012-10-12 14 views
34

Dato le seguenti classi:Valore di ritorno dell'operatore assegnazione nel codice concomitante

class Foo { 
    public volatile int number; 

    public int method1() { 
    int ret = number = 1; 
    return ret; 
    } 

    public int method2() { 
    int ret = number = 2; 
    return ret; 
    } 
} 

e dato più thread chiamando method1() e method2() contemporaneamente sullo stesso Foo esempio, può una chiamata a method1() mai restituire qualcosa di diverso 1?

risposta

10

I JLS 15.26 specifica:

Ci sono 12 operatori di assegnazione; tutti sono sintatticamente giusti-associativi (si raggruppano da destra a sinistra). Quindi, a = b = c significa a = (b = c), che assegna il valore di c a b e quindi assegna il valore di b a a.

La risposta di Ted Hopp mostra che il javac di Sun non segue questo comportamento, forse come un'ottimizzazione.

A causa della filettatura qui, il comportamento di Method1 sarebbe indefinito. Se il compilatore di Sun rende il comportamento costante, non si interrompe dal comportamento indefinito.

+0

Potrebbe essere consentita questa ottimizzazione per i campi ['volatile'] (http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4)? – trashgod

+0

Sono curioso però, se è quello che intendevano le specifiche. Penso che possano aver specificato b in "e quindi assegna il valore di b a a" solo per chiarire il tutto nel caso in cui a, b e c abbiano tipi diversi (diciamo tipi primitivi interi/floating) e le conversioni avvengano, l'assegnazione avviene come se fosse stato utilizzato il valore convertito. Ad esempio, se a e c erano doppi e b era un galleggiante. – BeeOnRope

+0

@BeeOnRope Questo è del tutto possibile: la pagina JLS sembra essere più interessata alle conversioni di tipo che ai campi volatili. – Bringer128

15

Penso che la risposta dipende dal compilatore. Il linguaggio specifies:

In fase di esecuzione, il risultato dell'espressione di assegnazione è il valore della variabile dopo il verificarsi l'assegnazione.

Suppongo che teoricamente il valore possa essere modificato prima che si verifichi la seconda (più a sinistra) assegnazione.

Tuttavia, con il compilatore javac di Sun, method1 si si trasformerà in:

0: aload_0 
1: iconst_1 
2: dup_x1 
3: putfield  #2; //Field number:I 
6: istore_1 
7: iload_1 
8: ireturn 

Questo duplica la costante 1 sullo stack e lo carica in number e poi in ret prima di ritornare ret. In questo caso, non importa se il valore memorizzato nella number viene modificata prima assegnazione al ret, perché 1, non number viene assegnato.

+0

Il linguaggio è confusa perché sembra implicare che il "valore" della variabile viene utilizzata, che sembra implicare che una seconda lettura è possibile? – BeeOnRope

+0

Il bytecode è certamente interessante, ma non dimostra ciò che è richiesto dalle specifiche. – BeeOnRope

+0

@BeeOnRope - In superficie, mi sembra che il codice generato dal compilatore non sia strettamente in accordo con le specifiche della lingua. Tuttavia, potrebbe essere che "dopo che l'assegnazione è avvenuta" significa "immediatamente dopo" e che eventuali ulteriori modifiche alla variabile da altri thread non influenzano i risultati.Se questo è ciò che si intende (e non mi è chiaro che sia), quindi javac sta generando il codice corretto, isolando il valore da modifiche simultanee a 'number'. –

5

O la dichiarazione contiene una lettura instabile, o non contiene una lettura volatile. Qui non può esserci alcuna ambiguità, dal momento che la lettura volatile è molto importante per programmare la semantica.

Se ci si può fidare di javac, possiamo concludere che la dichiarazione non implica una lettura volatile di number. Il valore di un'espressione di assegnazione x=y è in realtà solo il valore di y (dopo le conversioni).

Possiamo anche dedurre che

System.out.println(number=1); 

non comporta la lettura number

String s; 

    (s="hello").length(); 

non comporta la lettura s

x_1=x_2=...x_n=v 

non comporta la lettura x_n, x_n-1, ...; invece, il valore di v viene assegnato direttamente x_i (dopo necessarie conversioni attraverso tipi di x_n, ... x_i

+0

Spero tu abbia ragione, ma le specifiche non danno molto peso a questa interpretazione, vero? Penso che volatile sia un'aringa rossa. Potrebbe contenere una lettura non volatile e fare ancora cose strane con il threading (volatile significa che certe cose devono accadere, ma le stesse cose * possono * accadere con una lettura normale). – BeeOnRope

+0

Questa risposta mi sembra giusta in pratica.Il JLS afferma specificamente che 'a = b = c' è equivalente a' b = c; a = b; 'ma sembra che l'abbiano implementato come' b = c; a = c; 'per evitare la necessità di un lento recupero volatile del campo. Detto questo, il riferimento JLS citato potrebbe essere solo un povero esempio piuttosto che un * il compilatore deve fare questo * riferimento. – Bringer128