В примере, который эта книга дает классу Холдера, не является непосредственно причиной проблемы, фактически говорится, что:
Проблема здесь не в самом классе Holder, а в том, что Holder не опубликован должным образом. Тем не менее, Holder может быть защищен от неправильной публикации, если объявить поле n окончательным, что сделает Holder неизменным; см. раздел 3.5.2.
Непосредственно перед этим упоминается следующий код, который является предметом проблемы:
// Unsafe publication
public Holder holder;
public void initialize() {
holder = new Holder(42);
}
Таким образом, чтобы воссоздать его, вам нужно создать класс издателя и два потока, один из которых вызывает инициализацию, а другой - вызов утверждения.
Сказав это, я попытался воссоздать его сам, но все равно не смог :(
Ниже моя первая попытка, но есть лучшее объяснение проблемы на http://forums.oracle.com/forums/thread.jspa?threadID=1140814&tstart=195
public class HolderTest {
@Test
public void testHolder() throws Exception {
for (int i = 0; i < 1000000000; i++) {
final CountDownLatch finished = new CountDownLatch(2);
final HolderPublisher publisher = new HolderPublisher();
final Thread publisherThread = new Thread(new Publisher(publisher,
finished));
final Thread checkerThread = new Thread(new Checker(publisher,
finished));
publisher.holder = null;
publisherThread.start();
checkerThread.start();
finished.await();
}
}
static class Publisher implements Runnable {
private final CountDownLatch finished;
private final HolderPublisher publisher;
public Publisher(final HolderPublisher publisher,
final CountDownLatch finished) {
this.publisher = publisher;
this.finished = finished;
}
@Override
public void run() {
try {
publisher.initialize();
} finally {
finished.countDown();
}
}
}
static class Checker implements Runnable {
private final CountDownLatch finished;
private final HolderPublisher publisher;
public Checker(final HolderPublisher publisher,
final CountDownLatch finished) {
this.publisher = publisher;
this.finished = finished;
}
@Override
public void run() {
try {
publisher.holder.assertSanity();
} catch (final NullPointerException e) {
// This isnt the error we are interested in so swallow it
} finally {
finished.countDown();
}
}
}
static class HolderPublisher {
// Unsafe publication
public Holder holder;
public void initialize() {
holder = new Holder(42);
}
}
}