JDK-7020229 : ThreadLocal javadoc example is broken
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 6u23
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_7
  • CPU: x86
  • Submitted: 2011-02-17
  • Updated: 2012-09-06
  • Resolved: 2011-03-02
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :


A DESCRIPTION OF THE PROBLEM :
The example in http://download.oracle.com/javase/6/docs/api/java/lang/ThreadLocal.html
is:

 import java.util.concurrent.atomic.AtomicInteger;

 public class UniqueThreadIdGenerator {

     private static final AtomicInteger uniqueId = new AtomicInteger(0);

     private static final ThreadLocal < Integer > uniqueNum =
         new ThreadLocal < Integer > () {
             @Override protected Integer initialValue() {
                 return uniqueId.getAndIncrement();
         }
     };
 
     public static int getCurrentThreadId() {
         return uniqueId.get();
     }
 } // UniqueThreadIdGenerator

while it should be:

 import java.util.concurrent.atomic.AtomicInteger;

 public class UniqueThreadIdGenerator {

     private static final AtomicInteger uniqueId = new AtomicInteger(0);

     private static final ThreadLocal < Integer > uniqueNum =
         new ThreadLocal < Integer > () {
             @Override protected Integer initialValue() {
                 return uniqueId.getAndIncrement();
         }
     };
 
     public static int getCurrentThreadId() {
         return uniqueNum.get();
     }
 } // UniqueThreadIdGenerator


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run getCurrentThreadId() with many threads

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
getCurrentThreadID() should return a new id for each thread.
ACTUAL -
getCurrentThreadID() always returns 0.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import static org.junit.Assert.assertEquals;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

import org.junit.Test;

public class UniqueThreadIdGeneratorTest {
	// Broken UniqueThreadIdGenerator from
	// http://download.oracle.com/javase/6/docs/api/java/lang/ThreadLocal.html
	public static class UniqueThreadIdGenerator {

		private static final AtomicInteger uniqueId = new AtomicInteger(0);

		private static final ThreadLocal<Integer> uniqueNum = new ThreadLocal<Integer>() {
			@Override
			protected Integer initialValue() {
				return uniqueId.getAndIncrement();
			}
		};

		public static int getCurrentThreadId() {
			return uniqueId.get();
		}
	} // UniqueThreadIdGenerator

	@Test
	public void testThreadLocalUsage() throws Exception {
		final int numberOfThreads = 10;
		CountDownLatch counter = new CountDownLatch(numberOfThreads);
		ConcurrentSkipListSet<Integer> threadIds = new ConcurrentSkipListSet<Integer>();
		for (int i = 0; i < numberOfThreads; i++) {
			new Thread(createRunnable(threadIds, counter)).start();
		}
		counter.await();
		List<Integer> expectedThreadIds = Arrays.asList(new Integer[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
		assertEquals(expectedThreadIds, new ArrayList<Integer>(threadIds));
	}

	private Runnable createRunnable(final ConcurrentSkipListSet<Integer> threadIds, final CountDownLatch counter) {
		return new Runnable() {
			@Override
			public void run() {
				threadIds.add(UniqueThreadIdGenerator.getCurrentThreadId());
				counter.countDown();
			}
		};
	}
}

---------- END SOURCE ----------