[ 
https://issues.apache.org/jira/browse/COLLECTIONS-776?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17419513#comment-17419513
 ] 

ckurz commented on COLLECTIONS-776:
-----------------------------------

Hi [~amsadowski5] I can reproduce your behavior. 

If you add the following line in the while-loop the behavior will no longer 
occur:
{code}
wrappedMap.get(key);
{code}

Why is that? 
We actually have a three way decoration in this case SynchronizedMap --> 
PassiveExpiringMap --> HashMap
When calling {{wrappedMap.keySet()}} the synchronized map actually gets the 
keySet of the {{HashMap}}, through the {{PassiveExpiringMap}} (this is the only 
time the method {{PassiveExpiringMap#removeAllExpired}}). Every future call 
does not call {{PassiveExpiringMap#keySet}} but returns the Set of the actual 
HashMap. 
But if you e.g. call {{SynchronizedMap#get}}, 
{{PassiveExpiringMap#removeAllExpired}} will be called and the provided Set is 
updated.

> Wrapping PassiveExpiringMap in a SynchonizedMap breaks expiration
> -----------------------------------------------------------------
>
>                 Key: COLLECTIONS-776
>                 URL: https://issues.apache.org/jira/browse/COLLECTIONS-776
>             Project: Commons Collections
>          Issue Type: Bug
>          Components: Collection
>    Affects Versions: 4.4
>         Environment: Java 8
> Built on mac OS Catalina 10.15 using gradle 5.2.1 in Intellij
>            Reporter: Aidan Sadowski
>            Priority: Major
>
> The documentation for PassiveExpiringMap says "If you wish to use this map 
> from multiple threads concurrently, you must use appropriate synchronization. 
> The simplest approach is to wrap this map using 
> [{{Collections.synchronizedMap(Map)}}|https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html?is-external=true#synchronizedMap-java.util.Map-].";
> However, wrapping a PassiveExpiringMap in a Collections.synchronizedMap seems 
> to break the PassiveExpiringMap's expiration. Specifically, the operation 
> passiveExirpringMap.keySet() no longer removes expired entries prior to 
> returning (which it should, according to the PassiveExpiringMap doc).
> I wrote this simple program to prove it. It puts a key into two 
> PassiveExpiringMaps with the same expiration policy of 15 seconds, one of 
> which is wrapped in a SyncronizedMap. Then it loops through the maps, calling 
> keySet() on them. The entry disappears from the unwrapped map after 15 
> seconds, but NOT the wrapped map! Code and log output below.
>  
> {code:java}
> import java.text.SimpleDateFormat;
> import java.util.Collections;
> import java.util.Date;
> import java.util.Map;
> import java.util.Set;
> import java.util.UUID;
> import java.util.concurrent.TimeUnit;
> import org.apache.commons.collections4.map.PassiveExpiringMap;
> public class Main {
>     // unwrapped
>     static Map<String, String> unwrappedMap;
>     // wrapped
>     static Map<String, String> wrappedMap;
>     public static void main(String[] args) throws InterruptedException {
>         unwrappedMap = new PassiveExpiringMap<>(15, TimeUnit.SECONDS);
>         wrappedMap = Collections.synchronizedMap(new PassiveExpiringMap<>(15, 
> TimeUnit.SECONDS));
>         // Put something in the maps
>         String key = UUID.randomUUID().toString();
>         logWithTimestamp("Putting key " + key);
>         unwrappedMap.put(key, "");
>         wrappedMap.put(key, "");
>         // Check the map until the key has expired, sleeping 1 second between 
> checks
>         while (true) {
>             Set<String> keysInUnwrappedMap = unwrappedMap.keySet();
>             Set<String> keysInWrappedMap = wrappedMap.keySet();
>             logWithTimestamp("Keys in unwrapped map: " + String.join(", ", 
> keysInUnwrappedMap));
>             logWithTimestamp("Keys in wrapped map: " + String.join(", ", 
> keysInWrappedMap));
>             Thread.sleep(1000);
>         }
>     }
>     private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
>     private static void logWithTimestamp(String string) {
>         Date resultdate = new Date(System.currentTimeMillis());
>         System.out.println("[" + sdf.format(resultdate) + "] " + string);
>     }
> }
> {code}
> {code:java}
> [10:02:41] Putting key fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:41] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:41] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:42] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:42] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:43] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:43] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:44] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:44] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:45] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:45] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:46] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:46] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:47] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:47] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:48] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:48] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:49] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:49] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:50] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:50] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:51] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:51] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:52] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:52] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:53] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:53] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:54] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:54] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:55] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:55] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:56] Keys in unwrapped map: 
> [10:02:56] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:57] Keys in unwrapped map: 
> [10:02:57] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:58] Keys in unwrapped map: 
> [10:02:58] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> [10:02:59] Keys in unwrapped map: 
> [10:02:59] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
> {code}



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to