Fix | Fix SharedTimer implementation to use class level lock for thread safety
@@ -8,6 +8,7 @@ import java.io.Serializable;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
@@ -18,9 +19,9 @@ class SharedTimer implements Serializable {
private static final long serialVersionUID = -4069361613863955760L;
static final String CORE_THREAD_PREFIX = "mssql-jdbc-shared-timer-core-";
- private static final AtomicLong CORE_THREAD_COUNTER = new AtomicLong();
- private static SharedTimer instance;
+ private static final AtomicLong CORE_THREAD_COUNTER = new AtomicLong();
+ private static final Object lock = new Object();
/**
* Unique ID of this SharedTimer
*/
@@ -28,7 +29,9 @@ class SharedTimer implements Serializable {
/**
* Number of outstanding references to this SharedTimer
*/
- private int refCount = 0;
+ private final AtomicInteger refCount = new AtomicInteger();
+
+ private static volatile SharedTimer instance;
private ScheduledThreadPoolExecutor executor;
private SharedTimer() {
@@ -47,7 +50,7 @@ class SharedTimer implements Serializable {
/**
* @return Whether there is an instance of the SharedTimer currently allocated.
*/
- static synchronized boolean isRunning() {
+ static boolean isRunning() {
return instance != null;
}
@@ -56,18 +59,19 @@ class SharedTimer implements Serializable {
*
* If the reference count reaches zero then the underlying executor will be shutdown so that its thread stops.
*/
- public synchronized void removeRef() {
- if (refCount <= 0) {
+ public void removeRef() {
+ synchronized (lock) {
+ if (refCount.get() <= 0) {
throw new IllegalStateException("removeRef() called more than actual references");
}
- refCount -= 1;
- if (refCount == 0) {
+ if (refCount.decrementAndGet() == 0) {
// Removed last reference so perform cleanup
executor.shutdownNow();
executor = null;
instance = null;
}
}
+ }
/**
* Retrieve a reference to existing SharedTimer or create a new one.
@@ -76,14 +80,16 @@ class SharedTimer implements Serializable {
*
* When the caller is finished with the SharedTimer it must be released via {@link#removeRef}
*/
- public static synchronized SharedTimer getTimer() {
+ public static SharedTimer getTimer() {
+ synchronized (lock) {
if (instance == null) {
// No shared object exists so create a new one
instance = new SharedTimer();
}
- instance.refCount += 1;
+ instance.refCount.getAndIncrement();
return instance;
}
+ }
/**
* Schedule a task to execute in the future using this SharedTimer's internal executor.
