All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.atlassian.util.concurrent.WeakMemoizer Maven / Gradle / Ivy

Go to download

This project contains utility classes that are used by various products and projects inside Atlassian and may have some utility to the world at large.

There is a newer version: 4.0.1
Show newest version
/**
 * Copyright 2008 Atlassian Pty Ltd 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at 
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */

package io.atlassian.util.concurrent;

import net.jcip.annotations.ThreadSafe;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;

import static java.util.Objects.requireNonNull;

/**
 * {@link WeakMemoizer} caches the result of another function. The result is
 * {@link WeakReference weakly referenced} internally. This is useful if the
 * result is expensive to compute or the identity of the result is particularly
 * important.
 * 

* If the results from this function are further cached then they will tend to * stay in this cache for longer. * * @param comparable descriptor, the usual rules for any {@link HashMap} key * apply. * @param the value */ @ThreadSafe final class WeakMemoizer implements Function { static WeakMemoizer weakMemoizer(final Function delegate) { return new WeakMemoizer<>(delegate); } private final ConcurrentMap> map; private final ReferenceQueue queue = new ReferenceQueue(); private final Function delegate; /** * Construct a new {@link WeakMemoizer} instance. * * @param delegate for creating the initial values. * @throws IllegalArgumentException if the initial capacity of elements is * negative. */ WeakMemoizer(final @NotNull Function delegate) { this.map = new ConcurrentHashMap<>(); this.delegate = requireNonNull(delegate, "delegate"); } /** * Get a result for the supplied Descriptor. * * @param descriptor must not be null * @return descriptor lock */ public V apply(final K descriptor) { expungeStaleEntries(); requireNonNull(descriptor, "descriptor"); while (true) { final MappedReference reference = map.get(descriptor); if (reference != null) { final V value = reference.get(); if (value != null) { return value; } map.remove(descriptor, reference); } map.putIfAbsent(descriptor, new MappedReference<>(descriptor, delegate.apply(descriptor), queue)); } } // expunge entries whose value reference has been collected @SuppressWarnings("unchecked") private void expungeStaleEntries() { MappedReference ref; // /CLOVER:OFF while ((ref = (MappedReference) queue.poll()) != null) { final K key = ref.getDescriptor(); if (key == null) { // DO NOT REMOVE! In theory this should not be necessary as it // should not be able to be null - but we have seen it happen! continue; } map.remove(key, ref); } // /CLOVER:ON } /** * A weak reference that maintains a reference to the key so that it can be * removed from the map when the value is garbage collected. */ static final class MappedReference extends WeakReference { private final K key; public MappedReference(final K key, final V value, final ReferenceQueue q) { super(requireNonNull(value, "value"), q); this.key = requireNonNull(key, "key"); } final K getDescriptor() { return key; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy