### Question:
AbstractReferenceCounted implements ReferenceCounted { @Override public boolean release() { return release0(1); }  @Override int refCnt(); @Override ReferenceCounted retain(); @Override ReferenceCounted retain(int increment); @Override ReferenceCounted touch(); @Override boolean release(); @Override boolean release(int decrement);  }### Answer:
@Test public void testReleaseErrorMessage() { AbstractReferenceCounted referenceCounted = newReferenceCounted(); assertTrue(referenceCounted.release()); try { referenceCounted.release(1); fail("IllegalReferenceCountException didn't occur"); } catch (IllegalReferenceCountException e) { assertEquals("refCnt: 0, decrement: 1", e.getMessage()); } }
@Test(timeout = 30000) public void testReleaseFromMultipleThreadsThrowsReferenceCountException() throws Exception { int threads = 4; Queue<Future<?>> futures = new ArrayDeque<Future<?>>(threads); ExecutorService service = Executors.newFixedThreadPool(threads); final AtomicInteger refCountExceptions = new AtomicInteger(); try { for (int i = 0; i < 10000; i++) { final AbstractReferenceCounted referenceCounted = newReferenceCounted(); final CountDownLatch releaseLatch = new CountDownLatch(1); final AtomicInteger releasedCount = new AtomicInteger(); for (int a = 0; a < threads; a++) { final AtomicInteger releaseCnt = new AtomicInteger(0); futures.add(service.submit(new Runnable() { @Override public void run() { try { releaseLatch.await(); try { if (referenceCounted.release(releaseCnt.incrementAndGet())) { releasedCount.incrementAndGet(); } } catch (IllegalReferenceCountException e) { refCountExceptions.incrementAndGet(); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } })); } releaseLatch.countDown(); for (;;) { Future<?> f = futures.poll(); if (f == null) { break; } f.get(); } assertEquals(3, refCountExceptions.get()); assertEquals(1, releasedCount.get()); refCountExceptions.set(0); } } finally { service.shutdown(); } }