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

ru.fix.stdlib.reference.ReferenceCleaner Maven / Gradle / Ivy

There is a newer version: 3.1.4
Show newest version
package ru.fix.stdlib.reference;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
 * Use single static {@link ReferenceQueue} instance to track weak references.
 * Maintain single {@link Thread} to run clean action.
 * {@link Thread} will be created on demand and destroyed if there are no reference left to clean.
 * {@link Thread} will work in daemon mode {@link Thread#setDaemon(boolean)}
 */
public class ReferenceCleaner {
    private static final Logger log = LoggerFactory.getLogger(ReferenceCleaner.class);

    private ReferenceCleaner() {
    }

    private static final ReferenceCleaner INSTANCE = new ReferenceCleaner();

    public static ReferenceCleaner getInstance(){
        return INSTANCE;
    }


    private ReferenceQueue referenceQueue = new ReferenceQueue<>();
    private Set> createdReferences = Collections.newSetFromMap(new ConcurrentHashMap<>());
    private AtomicReference cleanerThread = new AtomicReference<>(null);

    private final class Reference
            extends WeakReference
            implements CleanableWeakReference {

        private final MetaType meta;
        private final BiConsumer, MetaType> cleaner;

        private Reference(ReferentType referent, MetaType meta,
                          BiConsumer, MetaType> cleaner,
                          ReferenceQueue referenceQueue) {
            super(referent, referenceQueue);
            this.meta = meta;
            this.cleaner = cleaner;
        }

        @Override
        public boolean cancelCleaningOrder() {
            return createdReferences.remove(this);
        }
    }

    /**
     * @param referent       object which reachability will be tracked through {@link ReferenceQueue}
     * @param metadata       will be passed to cleaning action
     * @param cleaningAction will be invoked when referent become weakly reachable
     * @param             referent type
     * @param             metadata type
     * @return reference interface that allows access to referent or cancel cleaning request
     */
    public  CleanableWeakReference register(T referent, M metadata, BiConsumer, M> cleaningAction) {
        final Reference ref = new Reference(referent, metadata, cleaningAction, referenceQueue);

        createdReferences.add(ref);
        ensureThreadExist();

        return ref;
    }

    private void ensureThreadExist() {
        if (cleanerThread.get() != null) return;

        CleanerThread newThread = new CleanerThread((thread)-> cleanerThread.compareAndSet(thread, null));

        if (cleanerThread.compareAndSet(null, newThread)) {
            newThread.start();
        }
    }

    private class CleanerThread extends Thread {
        private final Consumer onShutdown;

        public CleanerThread(Consumer onShutdown) {
            setName(ReferenceCleaner.class.getName());
            setDaemon(true);
            this.onShutdown = onShutdown;
        }

        @Override
        public void run() {
            try {
                while (!createdReferences.isEmpty() && !Thread.interrupted()) {
                    try {
                        Reference ref = (Reference) referenceQueue.remove();
                        if (ref != null) {
                            if (createdReferences.remove(ref)) {
                                ref.cleaner.accept(ref, ref.meta);
                            }
                        }
                    } catch (InterruptedException thr) {
                        log.debug("ReferenceCleaner thread interrupted.", thr);
                        return;
                    } catch (Exception thr) {
                        log.error("ReferenceCleaner cleaning thread failed", thr);
                    }
                }
            } finally {
                onShutdown.accept(this);
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy