8023310: Thread contention in the method Beans.IsDesignTime()
authormalenkov
Wed, 25 Sep 2013 14:06:15 +0400
changeset 20164 bcc676c18412
parent 20163 ac5a4f900445
child 20165 75e673bbdba6
8023310: Thread contention in the method Beans.IsDesignTime() Reviewed-by: art, sfriberg
jdk/src/share/classes/java/beans/ThreadGroupContext.java
jdk/src/share/classes/java/beans/WeakIdentityMap.java
--- 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);