1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.commons.id.uuid.clock; 19 20 21 /** 22 * <p>{@link Clock} provides a timing mechanism for returning the current time in 23 * 100-nano second intervals since 00:00:00.00, 15 October 1582.</p> 24 * 25 * <p>This Class consumes a single thread which will die off if not utilized before 26 * the expiration. Subsequent calls after a thread expires start a new thread. 27 * Compensates for jvm time resolution issues. This clock should be used in 28 * instances where the system resolution does not perform adequately - the 29 * clocking resolution on some windows virtual machines can range from 10 to 50 30 * milliseconds before the System.currentTimeMillis changes. In instances where 31 * 10,000 or more uuid's may be generated in a millisecond this Clock 32 * implementation may be required.</p> 33 * 34 * @author Commons-Id Team 35 * @version $Revision: 480488 $ $Date: 2006-11-29 08:57:26 +0000 (Wed, 29 Nov 2006) $ 36 */ 37 38 public final class ThreadClockImpl extends Thread implements Clock { 39 40 /** Default time to live of the Clock thread in milliseconds */ 41 public static final long DEFAULT_THREAD_LIFE = 200; 42 43 /** Life time of the clock thread in milliseconds */ 44 private static long threadLife = DEFAULT_THREAD_LIFE; 45 46 /** Singleton instance of the Clock thread*/ 47 private static ThreadClockImpl worker = null; 48 49 /** The current time in milliseconds held in this clock thread. */ 50 private static long currentTimeMillis; 51 52 /** Time when the clock thread should die */ 53 private static long expires = threadLife; 54 55 //---------------- Instance members --------------------- 56 /** The time in time milliseconds used in this instance */ 57 private long lastTimeMs = 0; 58 59 /** The counter for nanoseconds generated during this system interval(ms) */ 60 private int generatedThisInterval; 61 62 /** The system time interval to increment the clock */ 63 private static short sysInterval = 1; 64 // See bug parade 4814012, 4500388 65 static { 66 if (System.getProperty("os.name").indexOf("Windows") != -1) { 67 sysInterval = 10; 68 } 69 } 70 71 /** 72 * <p>Public constructor to instantiate a Clock instance.</p> 73 */ 74 public ThreadClockImpl() { 75 if (worker == null) { 76 synchronized (ThreadClockImpl.class) { 77 worker = new ThreadClockImpl(true); 78 } 79 } 80 } 81 82 /** 83 * Private constructor for clock implementation. Utilizes a single thread to 84 * increment the clock every milli seconds this should be more 85 * accurate than System.currentTimeMillis() as described in 86 * the javaworld article: 87 * http://www.javaworld.com/javaworld/javaqa/2003-01/01-qa-0110-timing.html 88 * 89 * @param isWorkerThread boolean indicating this is the worker thread. 90 */ 91 private ThreadClockImpl(boolean isWorkerThread) { 92 setDaemon(true); 93 setPriority(Thread.MAX_PRIORITY); 94 currentTimeMillis = System.currentTimeMillis(); 95 generatedThisInterval = 0; 96 start(); 97 } 98 99 /** 100 * Returns the thread life in milliseconds. If the clock thread is not 101 * accessed within this time span the thread will die off. 102 * 103 * @return thread life time span in milliseconds 104 */ 105 public static long getThreadLife() { 106 return ThreadClockImpl.threadLife; 107 } 108 109 /** 110 * @param threadLifeLen milliseconds this thread should live for. Each 111 * call to getCurrentTime resets the expiration time value. 112 */ 113 public static void setThreadLife(long threadLifeLen) { 114 ThreadClockImpl.threadLife = threadLifeLen; 115 } 116 117 /** 118 * Threads run method that increments the clock and resets the generated 119 * nano seconds counter. 120 */ 121 public void run() { 122 try { 123 while (--expires >= 0) { 124 sleep(sysInterval); 125 synchronized (ThreadClockImpl.class) { 126 currentTimeMillis += sysInterval; 127 } 128 } 129 } catch (InterruptedException e) { 130 System.out.println("Clock thread interrupted"); 131 } 132 } 133 134 /** 135 * Returns the current time as described in the clock resolution and 136 * timestamp sections of the uuid specification. 137 * 138 * @return the current time in 100-nano second intervals (simulated) 139 * @throws OverClockedException an exception when the number of timestamps 140 * generated exceeds the allowable timestamps for the system time 141 * interval. 142 */ 143 private synchronized long getTimeSynchronized() 144 throws OverClockedException { 145 long current = 0; 146 synchronized (ThreadClockImpl.class) { 147 current = currentTimeMillis; 148 } 149 if (current != lastTimeMs) { 150 generatedThisInterval = 0; 151 lastTimeMs = current; 152 } 153 if (generatedThisInterval + 1 >= (INTERVALS_PER_MILLI * sysInterval)) { 154 throw new OverClockedException(); 155 } 156 return ((current + GREGORIAN_CHANGE_OFFSET) * INTERVALS_PER_MILLI) 157 + generatedThisInterval++; 158 } 159 160 /** 161 * Method returns the clocks current time in 100-nanosecond intervals 162 * since the Gregorian calander change. Calendar.GREGORIAN_OFFSET 163 * 164 * @return Coordinated Universal Time (UTC) as a count of 100- nanosecond 165 * intervals since 00:00:00.00, 15 October 1582. 166 * @throws OverClockedException an exception when the number of timestamps 167 * generated exceeds the allowable timestamps for the system time 168 * interval. 169 */ 170 public long getUUIDTime() throws OverClockedException { 171 if (!worker.isAlive()) { 172 synchronized (SystemClockImpl.class) { 173 currentTimeMillis = System.currentTimeMillis(); 174 worker.start(); 175 } 176 generatedThisInterval = 0; 177 } 178 return getTimeSynchronized(); 179 } 180 }