2121import java .lang .management .ThreadMXBean ;
2222import java .util .Optional ;
2323import java .util .concurrent .TimeUnit ;
24+ import net .jcip .annotations .NotThreadSafe ;
2425import org .slf4j .Logger ;
2526import org .slf4j .LoggerFactory ;
2627
2930 * {@link java.lang.management.ThreadMXBean}.
3031 *
3132 * <p>Calling code should create an instance of this class when starting the operation, and then can
32- * get the {@link #getCpuTimeMs()} at any time thereafter.
33+ * get the {@link #getElapsedCpuMs()} at any time thereafter.
34+ *
35+ * <p>This class is irrevocably not thread safe. Never allow instances of this class to be exposed
36+ * to more than one thread. Acquiring an external lock will not be sufficient. This class can be
37+ * considered "lock-hostile" due to its caching of timing information for a specific thread.
3338 */
39+ @ NotThreadSafe
3440public class ThreadCpuTimer {
3541 private static final long UNSUPPORTED = -1 ;
3642 public static final String CPU_TIME = "cpuTime" ;
@@ -56,61 +62,59 @@ public class ThreadCpuTimer {
5662 private final long startCpuTimeNanos ;
5763
5864 /**
59- * Create an instance to track the current thread's usage of CPU. The usage information can later
60- * be retrieved by any thread by calling {@link #getCpuTimeMs ()}.
65+ * Create an instance to track the current thread's usage of CPU. Usage information can later be
66+ * retrieved by calling {@link #getElapsedCpuMs ()}. Timing starts immediately upon construction .
6167 */
6268 public ThreadCpuTimer () {
63- if (THREAD_MX_BEAN != null ) {
64- this .startCpuTimeNanos = THREAD_MX_BEAN .getCurrentThreadCpuTime ();
65- } else {
66- this .startCpuTimeNanos = UNSUPPORTED ;
67- }
69+ this .startCpuTimeNanos = getThreadTotalCpuNs ();
6870 }
6971
7072 public static boolean isSupported () {
7173 return THREAD_MX_BEAN != null ;
7274 }
7375
7476 /**
75- * Return the initial value of CPU time for this thread when this instance was first created.
76- * NOTE: absolute value returned by this method has no meaning by itself, it should only be used
77- * when comparing elapsed time between this value and {@link #getCurrentCpuTimeNs()}.
77+ * Return CPU time consumed by this thread since the construction of this timer object.
7878 *
7979 * @return current value, or {@link #UNSUPPORTED} if not supported.
8080 */
81- public long getStartCpuTimeNs () {
82- return startCpuTimeNanos ;
81+ public long getElapsedCpuNs () {
82+ return this .startCpuTimeNanos != UNSUPPORTED
83+ ? getThreadTotalCpuNs () - this .startCpuTimeNanos
84+ : UNSUPPORTED ;
8385 }
8486
8587 /**
86- * Return current value of CPU time for this thread.
88+ * Get the cpu time for the current thread since {@link Thread#start()} without throwing an
89+ * exception.
8790 *
88- * @return current value, or {@link #UNSUPPORTED} if not supported.
91+ * @see ThreadMXBean#getCurrentThreadCpuTime() for important details
92+ * @return the number of nanoseconds of cpu consumed by this thread since {@code Thread.start()}.
8993 */
90- public long getCurrentCpuTimeNs () {
94+ private long getThreadTotalCpuNs () {
9195 if (THREAD_MX_BEAN != null ) {
92- return this .startCpuTimeNanos != UNSUPPORTED
93- ? THREAD_MX_BEAN .getCurrentThreadCpuTime () - this .startCpuTimeNanos
94- : UNSUPPORTED ;
96+ return THREAD_MX_BEAN .getCurrentThreadCpuTime ();
9597 } else {
9698 return UNSUPPORTED ;
9799 }
98100 }
99101
100102 /**
101- * Get the CPU usage information for the thread that created this {@link ThreadCpuTimer}. The
102- * information will track the thread's cpu since the creation of this {@link ThreadCpuTimer}
103- * instance, if the VM's cpu tracking is disabled, returned value will be {@link #UNSUPPORTED}.
103+ * Get the CPU usage information for the current thread since it created this {@link
104+ * ThreadCpuTimer}. The result is undefined if called by any other thread.
105+ *
106+ * @return the thread's cpu since the creation of this {@link ThreadCpuTimer} instance. If the
107+ * VM's cpu tracking is disabled, returned value will be {@link #UNSUPPORTED}.
104108 */
105- public Optional <Long > getCpuTimeMs () {
106- long cpuTimeNs = getCurrentCpuTimeNs ();
109+ public Optional <Long > getElapsedCpuMs () {
110+ long cpuTimeNs = getElapsedCpuNs ();
107111 return cpuTimeNs != UNSUPPORTED
108112 ? Optional .of (TimeUnit .MILLISECONDS .convert (cpuTimeNs , TimeUnit .NANOSECONDS ))
109- : Optional .of ( UNSUPPORTED );
113+ : Optional .empty ( );
110114 }
111115
112116 @ Override
113117 public String toString () {
114- return getCpuTimeMs ().toString ( );
118+ return getElapsedCpuMs ().map ( String :: valueOf ). orElse ( "UNSUPPORTED" );
115119 }
116120}
0 commit comments