|
12 | 12 | import java.util.concurrent.ScheduledExecutorService; |
13 | 13 | import java.util.concurrent.ThreadFactory; |
14 | 14 | import java.util.concurrent.TimeUnit; |
| 15 | +import java.util.concurrent.atomic.AtomicBoolean; |
15 | 16 |
|
16 | 17 | class WeakMapSuppliers { |
17 | 18 | // Comparison with using WeakConcurrentMap vs Guava's implementation: |
@@ -57,55 +58,86 @@ public Thread newThread(final Runnable r) { |
57 | 58 | private final Queue<WeakReference<WeakConcurrentMap>> suppliedMaps = |
58 | 59 | new ConcurrentLinkedQueue<>(); |
59 | 60 |
|
60 | | - private final Runnable runnable = |
61 | | - new Runnable() { |
62 | | - @Override |
63 | | - public void run() { |
64 | | - for (final Iterator<WeakReference<WeakConcurrentMap>> iterator = |
65 | | - suppliedMaps.iterator(); |
66 | | - iterator.hasNext(); ) { |
67 | | - final WeakConcurrentMap map = iterator.next().get(); |
68 | | - if (map == null) { |
69 | | - iterator.remove(); |
70 | | - } else { |
71 | | - map.expungeStaleEntries(); |
72 | | - } |
73 | | - } |
74 | | - } |
75 | | - }; |
| 61 | + private final AtomicBoolean finalized = new AtomicBoolean(false); |
76 | 62 |
|
77 | 63 | WeakConcurrent() { |
78 | 64 | cleanerExecutorService = Executors.newScheduledThreadPool(1, THREAD_FACTORY); |
79 | 65 | cleanerExecutorService.scheduleAtFixedRate( |
80 | | - runnable, CLEAN_FREQUENCY_SECONDS, CLEAN_FREQUENCY_SECONDS, TimeUnit.SECONDS); |
| 66 | + new CleanupRunnable(cleanerExecutorService, suppliedMaps, finalized), |
| 67 | + CLEAN_FREQUENCY_SECONDS, |
| 68 | + CLEAN_FREQUENCY_SECONDS, |
| 69 | + TimeUnit.SECONDS); |
81 | 70 |
|
82 | 71 | try { |
83 | | - Runtime.getRuntime() |
84 | | - .addShutdownHook( |
85 | | - new Thread() { |
86 | | - @Override |
87 | | - public void run() { |
88 | | - try { |
89 | | - cleanerExecutorService.shutdownNow(); |
90 | | - cleanerExecutorService.awaitTermination( |
91 | | - SHUTDOWN_WAIT_SECONDS, TimeUnit.SECONDS); |
92 | | - } catch (final InterruptedException e) { |
93 | | - // Don't bother waiting then... |
94 | | - } |
95 | | - } |
96 | | - }); |
| 72 | + Runtime.getRuntime().addShutdownHook(new ShutdownCallback(cleanerExecutorService)); |
97 | 73 | } catch (final IllegalStateException ex) { |
98 | 74 | // The JVM is already shutting down. |
99 | 75 | } |
100 | 76 | } |
101 | 77 |
|
| 78 | + @Override |
| 79 | + public void finalize() { |
| 80 | + finalized.set(true); |
| 81 | + } |
| 82 | + |
102 | 83 | @Override |
103 | 84 | public <K, V> WeakMap<K, V> get() { |
104 | 85 | final WeakConcurrentMap<K, V> map = new WeakConcurrentMap<>(false); |
105 | 86 | suppliedMaps.add(new WeakReference<WeakConcurrentMap>(map)); |
106 | 87 | return new Adapter<>(map); |
107 | 88 | } |
108 | 89 |
|
| 90 | + private static class CleanupRunnable implements Runnable { |
| 91 | + |
| 92 | + private final ScheduledExecutorService executorService; |
| 93 | + private final Queue<WeakReference<WeakConcurrentMap>> suppliedMaps; |
| 94 | + private final AtomicBoolean finalized; |
| 95 | + |
| 96 | + public CleanupRunnable( |
| 97 | + final ScheduledExecutorService executorService, |
| 98 | + final Queue<WeakReference<WeakConcurrentMap>> suppliedMaps, |
| 99 | + final AtomicBoolean finalized) { |
| 100 | + this.executorService = executorService; |
| 101 | + this.suppliedMaps = suppliedMaps; |
| 102 | + this.finalized = finalized; |
| 103 | + } |
| 104 | + |
| 105 | + @Override |
| 106 | + public void run() { |
| 107 | + for (final Iterator<WeakReference<WeakConcurrentMap>> iterator = suppliedMaps.iterator(); |
| 108 | + iterator.hasNext(); ) { |
| 109 | + final WeakConcurrentMap map = iterator.next().get(); |
| 110 | + if (map == null) { |
| 111 | + iterator.remove(); |
| 112 | + } else { |
| 113 | + map.expungeStaleEntries(); |
| 114 | + } |
| 115 | + } |
| 116 | + if (finalized.get() && suppliedMaps.isEmpty()) { |
| 117 | + executorService.shutdown(); |
| 118 | + } |
| 119 | + } |
| 120 | + } |
| 121 | + |
| 122 | + private static final class ShutdownCallback extends Thread { |
| 123 | + |
| 124 | + private final ScheduledExecutorService executorService; |
| 125 | + |
| 126 | + public ShutdownCallback(final ScheduledExecutorService executorService) { |
| 127 | + this.executorService = executorService; |
| 128 | + } |
| 129 | + |
| 130 | + @Override |
| 131 | + public void run() { |
| 132 | + try { |
| 133 | + executorService.shutdownNow(); |
| 134 | + executorService.awaitTermination(SHUTDOWN_WAIT_SECONDS, TimeUnit.SECONDS); |
| 135 | + } catch (final InterruptedException e) { |
| 136 | + // Don't bother waiting then... |
| 137 | + } |
| 138 | + } |
| 139 | + } |
| 140 | + |
109 | 141 | private static class Adapter<K, V> implements WeakMap<K, V> { |
110 | 142 | private final WeakConcurrentMap<K, V> map; |
111 | 143 |
|
@@ -150,7 +182,7 @@ public <K, V> WeakMap<K, V> get() { |
150 | 182 | return new WeakMap.MapAdapter<>(new MapMaker().weakKeys().<K, V>makeMap()); |
151 | 183 | } |
152 | 184 |
|
153 | | - public <K, V> WeakMap<K, V> get(int concurrencyLevel) { |
| 185 | + public <K, V> WeakMap<K, V> get(final int concurrencyLevel) { |
154 | 186 | return new WeakMap.MapAdapter<>( |
155 | 187 | new MapMaker().concurrencyLevel(concurrencyLevel).weakKeys().<K, V>makeMap()); |
156 | 188 | } |
|
0 commit comments