thread safe UUID generator

So, I am thinking about a utility class to generate UUIDs, basically I will just leverage the JDK UUID class.  I want to make sure 1) decent performance 2) must be thread-safe. The UUID.randomUUID() can generate random numbers like “1fd7da4d-e2e1-4bb2-accc-37dde169fa41″ . I am not too concerned of the possiblity of duplicate ids (the chance is pretty low based on what I read from other sources). It will takes about 100ms on my laptop (Dell E6400) to generate ONE uuid – mostly because of the overhead of network looking up. It will take no time to generate the 2nd, 3rd, … ids.

It can just stop here, but somehow I want to punish myself and make it more complicated…(really, unnecessarily

My plan is to pre-generate thoudands of ready-to-use uuids, put in a queue, and serve the requests. And I want to try it in multi-threads environment too. Anyway, below is what I have done:

public enum UniqueIdGenerator {

	instance;
    private final ReentrantLock  lock = new ReentrantLock ();
	private static final int batchSize = 5000;
	private BlockingQueue<UUID> uuids = new LinkedBlockingQueue<UUID>();
	private UniqueIdGenerator() {
		this.populdate();
	}

	private void populdate(){

		System.out.println("populate uuid generator...");
		for(int i=0;i<batchSize;i++){
			uuids.add(UUID.randomUUID());
		}
	}

	public UUID getUUID(){

		UUID uuid = null;
		while(lock.isLocked()){
			try {
				Thread.currentThread().sleep(30);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		try {
			uuid = this.uuids.take();
			if(uuids.peek()==null){
				if(lock.tryLock()){
					try{
						this.populdate();
					}finally{
						lock.unlock();
					}
				}
			}
		} catch (InterruptedException e) {
			Thread.currentThread().interrupt();
		}

		if(this.uuids.size()>batchSize){
			throw new RuntimeException("uuids exceeds " + batchSize + "!");
		}
		if(uuid==null){
			throw new RuntimeException("uuid not available!");
		}
		return uuid;
	}
}

To use it, just call “UniqueIdGenerator.getUUID()”. Below is the multi-threads testing

public void testMultiThreads(){
		//create N threads, all keep reading the uuid generator and put the returned uuid into a Set in main thread which will blow if
		//duplicate value added. Also, check whether the UUID queue is populated at same time by threads
		int threadPoolSize = 300;
		System.out.println("main thread:about to create the threads and run ....");
		ExecutorService exec = (ExecutorService) Executors.newFixedThreadPool(threadPoolSize);
		List<Future<List<UUID>>> tasks = new LinkedList<Future<List<UUID>>>();
		for(int i=0;i<threadPoolSize;i++){

			Callable<List<UUID>> workerTask = new Callable<List<UUID>>(){
				List<UUID> uuids = new LinkedList<UUID>();
				public List<UUID> call() throws Exception {
					System.out.println("Thread " + Thread.currentThread().getName() + " started....");
					for(int i=0;i<1000;i++){
						try{
							//System.out.println("Thread " + Thread.currentThread().getName() + " get " + (i+1));
						uuids.add(UniqueIdGenerator.instance.getUUID());
						}catch(Exception e){
							System.out.println("Thread " + Thread.currentThread().getName() + " exception....");
							e.printStackTrace();
						}
					}
					System.out.println("Thread " + Thread.currentThread().getName() + " ended");
					return uuids;
				}
			};

			Future<List<UUID>> task = exec.submit(workerTask);
			tasks.add(task);
		}
		//
		long t1 = System.currentTimeMillis();
		System.out.println("totally tasks:" + tasks.size());
		//now check until all done
		boolean allDone = false;
		Set<UUID> allIds = new HashSet();
		while(!allDone){
			ListIterator<Future<List<UUID>>> ite = tasks.listIterator();
			while(ite.hasNext()){
				Future<List<UUID>> task = ite.next();

				if(task.isDone()){
					//add all results together
					try {

						allIds.addAll(task.get());
//						System.out.println("total:" + allIds.size());
						//remove task from list
						ite.remove();
					} catch (InterruptedException e) {
						e.printStackTrace();
					} catch (ExecutionException e) {
						e.printStackTrace();
					}
				}

			}
			//System.out.println("running threads:" + tasks.size());
			if(tasks.isEmpty()){
				allDone=true;
			}else{
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		long t2 = System.currentTimeMillis();
		System.out.println("total time " + (t2-t1) + " average:" + (t2-t1)/threadPoolSize);
		System.out.println("all done");
		System.out.println("total:" + allIds.size());

	}

I started 300 threads, each of which keep generating totally 1000 UUIDs through calling the singelton. The interesting part here is, it is not a typical producer-consumer model. All the 300 threads are consumers and when the queue is empty, one and only one of them will take the “producer” role – set the lock and re-generate batch of UUIDs.
It actually took me for a while to get the point the above concurrency testing worked as expected – the lession? programming multi-threads in Java is HARD, even with the concurrent package.  I had to constantly stop and think of the different scenarios of which threads are doing what. The root cause is the shared data, the shared queue. No kidding. This stuff is difficult to make right.

Advertisements

2 thoughts on “thread safe UUID generator

  1. Why check the lock in a while loop? Why not just call .lock(), and it will just automatically block until the lock becomes available?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s