8023310: Thread contention in the method Beans.IsDesignTime()
Reviewed-by: art, sfriberg
--- a/jdk/src/share/classes/java/beans/ThreadGroupContext.java Tue Sep 24 18:24:03 2013 +0400
+++ b/jdk/src/share/classes/java/beans/ThreadGroupContext.java Wed Sep 25 14:06:15 2013 +0400
@@ -41,24 +41,20 @@
*/
final class ThreadGroupContext {
- private static final WeakIdentityMap<ThreadGroupContext> contexts = new WeakIdentityMap<>();
+ private static final WeakIdentityMap<ThreadGroupContext> contexts = new WeakIdentityMap<ThreadGroupContext>() {
+ protected ThreadGroupContext create(Object key) {
+ return new ThreadGroupContext();
+ }
+ };
/**
- * Returns the appropriate {@code AppContext} for the caller,
+ * Returns the appropriate {@code ThreadGroupContext} for the caller,
* as determined by its {@code ThreadGroup}.
*
* @return the application-dependent context
*/
static ThreadGroupContext getContext() {
- ThreadGroup group = Thread.currentThread().getThreadGroup();
- synchronized (contexts) {
- ThreadGroupContext context = contexts.get(group);
- if (context == null) {
- context = new ThreadGroupContext();
- contexts.put(group, context);
- }
- return context;
- }
+ return contexts.get(Thread.currentThread().getThreadGroup());
}
private volatile boolean isDesignTime;
--- a/jdk/src/share/classes/java/beans/WeakIdentityMap.java Tue Sep 24 18:24:03 2013 +0400
+++ b/jdk/src/share/classes/java/beans/WeakIdentityMap.java Wed Sep 25 14:06:15 2013 +0400
@@ -33,18 +33,22 @@
* and reference-equality in place of object-equality to compare them.
* An entry will automatically be removed when its key is no longer
* in ordinary use. Both null values and the null key are supported.
+ * This class does not require additional synchronization.
+ * A thread-safety is provided by a fragile combination
+ * of synchronized blocks and volatile fields.
+ * Be very careful during editing!
*
* @see java.util.IdentityHashMap
* @see java.util.WeakHashMap
*/
-final class WeakIdentityMap<T> {
+abstract class WeakIdentityMap<T> {
private static final int MAXIMUM_CAPACITY = 1 << 30; // it MUST be a power of two
private static final Object NULL = new Object(); // special object for null key
private final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
- private Entry<T>[] table = newTable(1<<3); // table's length MUST be a power of two
+ private volatile Entry<T>[] table = newTable(1<<3); // table's length MUST be a power of two
private int threshold = 6; // the next size value at which to resize
private int size = 0; // the number of key-value mappings
@@ -54,78 +58,83 @@
key = NULL;
}
int hash = key.hashCode();
- int index = getIndex(this.table, hash);
- for (Entry<T> entry = this.table[index]; entry != null; entry = entry.next) {
+ Entry<T>[] table = this.table;
+ // unsynchronized search improves performance
+ // the null value does not mean that there are no needed entry
+ int index = getIndex(table, hash);
+ for (Entry<T> entry = table[index]; entry != null; entry = entry.next) {
if (entry.isMatched(key, hash)) {
return entry.value;
}
}
- return null;
+ synchronized (NULL) {
+ // synchronized search improves stability
+ // we must create and add new value if there are no needed entry
+ index = getIndex(this.table, hash);
+ for (Entry<T> entry = this.table[index]; entry != null; entry = entry.next) {
+ if (entry.isMatched(key, hash)) {
+ return entry.value;
+ }
+ }
+ T value = create(key);
+ this.table[index] = new Entry<T>(key, hash, value, this.queue, this.table[index]);
+ if (++this.size >= this.threshold) {
+ if (this.table.length == MAXIMUM_CAPACITY) {
+ this.threshold = Integer.MAX_VALUE;
+ }
+ else {
+ removeStaleEntries();
+ table = newTable(this.table.length * 2);
+ transfer(this.table, table);
+ // If ignoring null elements and processing ref queue caused massive
+ // shrinkage, then restore old table. This should be rare, but avoids
+ // unbounded expansion of garbage-filled tables.
+ if (this.size >= this.threshold / 2) {
+ this.table = table;
+ this.threshold *= 2;
+ }
+ else {
+ transfer(table, this.table);
+ }
+ }
+ }
+ return value;
+ }
}
- public T put(Object key, T value) {
- removeStaleEntries();
- if (key == null) {
- key = NULL;
- }
- int hash = key.hashCode();
- int index = getIndex(this.table, hash);
- for (Entry<T> entry = this.table[index]; entry != null; entry = entry.next) {
- if (entry.isMatched(key, hash)) {
- T oldValue = entry.value;
- entry.value = value;
- return oldValue;
- }
- }
- this.table[index] = new Entry<T>(key, hash, value, this.queue, this.table[index]);
- if (++this.size >= this.threshold) {
- if (this.table.length == MAXIMUM_CAPACITY) {
- this.threshold = Integer.MAX_VALUE;
- }
- else {
- removeStaleEntries();
- Entry<T>[] table = newTable(this.table.length * 2);
- transfer(this.table, table);
-
- // If ignoring null elements and processing ref queue caused massive
- // shrinkage, then restore old table. This should be rare, but avoids
- // unbounded expansion of garbage-filled tables.
- if (this.size >= this.threshold / 2) {
- this.table = table;
- this.threshold *= 2;
- }
- else {
- transfer(table, this.table);
- }
- }
- }
- return null;
- }
+ protected abstract T create(Object key);
private void removeStaleEntries() {
- for (Object ref = this.queue.poll(); ref != null; ref = this.queue.poll()) {
- @SuppressWarnings("unchecked")
- Entry<T> entry = (Entry<T>) ref;
- int index = getIndex(this.table, entry.hash);
+ Object ref = this.queue.poll();
+ if (ref != null) {
+ synchronized (NULL) {
+ do {
+ @SuppressWarnings("unchecked")
+ Entry<T> entry = (Entry<T>) ref;
+ int index = getIndex(this.table, entry.hash);
- Entry<T> prev = this.table[index];
- Entry<T> current = prev;
- while (current != null) {
- Entry<T> next = current.next;
- if (current == entry) {
- if (prev == entry) {
- this.table[index] = next;
+ Entry<T> prev = this.table[index];
+ Entry<T> current = prev;
+ while (current != null) {
+ Entry<T> next = current.next;
+ if (current == entry) {
+ if (prev == entry) {
+ this.table[index] = next;
+ }
+ else {
+ prev.next = next;
+ }
+ entry.value = null; // Help GC
+ entry.next = null; // Help GC
+ this.size--;
+ break;
+ }
+ prev = current;
+ current = next;
}
- else {
- prev.next = next;
- }
- entry.value = null; // Help GC
- entry.next = null; // Help GC
- this.size--;
- break;
+ ref = this.queue.poll();
}
- prev = current;
- current = next;
+ while (ref != null);
}
}
}
@@ -164,8 +173,8 @@
private static class Entry<T> extends WeakReference<Object> {
private final int hash;
- private T value;
- private Entry<T> next;
+ private volatile T value;
+ private volatile Entry<T> next;
Entry(Object key, int hash, T value, ReferenceQueue<Object> queue, Entry<T> next) {
super(key, queue);