Estoy aprendiendo el código fuente de Java, cuando leo el código fuente de ConcurrentHashMap, estoy confundido con el método initTable(), ¿por qué verificar (tab = table) == null || tab.length == 0 dos veces, primero en while() , luego en if() . No puedo imaginar en qué situación necesita el segundo control.
Creo que tal vez sea porque la JVM reordenó el código, puso sizeCtl = sc; delante de Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n]; . Es solo mi suposición, no sé si es correcto.
Alguien me lo puede explicar, muchas gracias.
private final Node<K,V>[] initTable() { Node<K,V>[] tab; int sc; while ((tab = table) == null || tab.length == 0) { if ((sc = sizeCtl) < 0) Thread.yield(); // lost initialization race; just spin else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) { try { if ((tab = table) == null || tab.length == 0) { int n = (sc > 0) ? sc : DEFAULT_CAPACITY; @SuppressWarnings("unchecked") Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n]; table = tab = nt; sc = n - (n >>> 2); } } finally { sizeCtl = sc; } break; } } return tab; }Múltiples hilos pueden competir para hacer esto (vea el comentario de "carrera de inicialización").
Parafraseando el código:
while(uninitialized) { acquire_lock(); //compareAndSwapInt... if(uninitialized) { do_init(); } } El control externo es una prueba "desbloqueada" barata. El interno es en caso de que alguien más ya haya tenido éxito entre while y compareAndSwapInt .