# HG changeset patch # User mcherkas # Date 1380807121 -14400 # Node ID 5d89c18a26b8447efdf4aae71b7534afef00390f # Parent 02cc213008c4cc2a5e8974bdc2c0ca7418026017 8023310: Thread contention in the method Beans.IsDesignTime() Reviewed-by: alexp, malenkov diff -r 02cc213008c4 -r 5d89c18a26b8 src/share/classes/java/beans/ThreadGroupContext.java --- a/src/share/classes/java/beans/ThreadGroupContext.java Thu Nov 21 13:39:01 2013 +0000 +++ b/src/share/classes/java/beans/ThreadGroupContext.java Thu Oct 03 17:32:01 2013 +0400 @@ -41,24 +41,20 @@ */ final class ThreadGroupContext { - private static final WeakIdentityMap contexts = new WeakIdentityMap<>(); + private static final WeakIdentityMap contexts = new WeakIdentityMap() { + 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; diff -r 02cc213008c4 -r 5d89c18a26b8 src/share/classes/java/beans/WeakIdentityMap.java --- a/src/share/classes/java/beans/WeakIdentityMap.java Thu Nov 21 13:39:01 2013 +0000 +++ b/src/share/classes/java/beans/WeakIdentityMap.java Thu Oct 03 17:32:01 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 { +abstract class WeakIdentityMap { 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 queue = new ReferenceQueue(); - private Entry[] table = newTable(1<<3); // table's length MUST be a power of two + private volatile Entry[] 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 entry = this.table[index]; entry != null; entry = entry.next) { + Entry[] 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 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 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(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 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(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[] 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 entry = (Entry) ref; - int index = getIndex(this.table, entry.hash); + Object ref = this.queue.poll(); + if (ref != null) { + synchronized (NULL) { + do { + @SuppressWarnings("unchecked") + Entry entry = (Entry) ref; + int index = getIndex(this.table, entry.hash); - Entry prev = this.table[index]; - Entry current = prev; - while (current != null) { - Entry next = current.next; - if (current == entry) { - if (prev == entry) { - this.table[index] = next; + Entry prev = this.table[index]; + Entry current = prev; + while (current != null) { + Entry 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 extends WeakReference { private final int hash; - private T value; - private Entry next; + private volatile T value; + private volatile Entry next; Entry(Object key, int hash, T value, ReferenceQueue queue, Entry next) { super(key, queue);