package java.lang.ref;
import java.util.Objects;
import java.util.function.Consumer;
/**
*
Finalizator provides an alternative form of finalization which can be more
* efficiently combined with (almost) regular manual cleanup where it serves as
* a last-resort cleanup mechanism when manual cleanup is not performed.
*
* Instead of overriding the Object's {@link #finalize()} method, a class
* arranges in it's constructor(s) to {@link #create(Object, Consumer) create} a
* {@code Finalizator} object which is used by GC to track the reachability of
* given {@code finalizee} and invoke given {@code thunk} with it when such
* {@code finalizee} becomes unreachable.
*
* {@code Finalizator} can be {@link #clean() invoked} manually by user code that
* decides to perform cleanup even before GC invokes it. The 1st invocation of
* {@link #clean()} method performs the cleanup by invoking the {@code thunk},
* subsequent invocations are ignored.
*
* After the cleanup is performed, the
* {@code Finalizator} is {@link #clear() cleared} which breaks the links between:
*
* - finalizator and finalizee,
* - finalizator and thunk,
* - platform and finalizator
*
* In the absence of other links, finalizator, finalizee and thunk become
* unreachable and eligible for GC.
*
* Here's an example of a classic finalizable class:
*
{@code
* public class Classic {
* @Override
* protected void finalize() {
* // clean-up actions invoked at most once...
* }
* }
* }
*
* And this is an alternative using {@code Finalizator}, combining finalization
* with manual cleanup:
*
{@code
* public class Alternative {
* private final Finalizator finalizator =
* Finalizator.create(this, Alternative::cleanup);
*
* void cleanup() {
* // clean-up actions invoked at most once...
* }
*
* // manually triggered cleanup
* public void close() {
* finalizator.run();
* }
* }
* }
*
* @param the type of finalizee tracked by Finalizator
* @since 1.9
*/
public final class Finalizator extends Finalizer implements Cleaner {
private Consumer super T> thunk;
/**
* Creates and returns an instance of Finalizator used by GC to track given
* {@code finalizee} and invoke given {@code thunk} with it when the
* finalizee becomes unreachable.
*
* @param finalizee the instance to track it's reachability
* @param thunk a {@link Consumer} which is invoked and passed the
* {@code finalizee} when it becomes unreachable.
* @param the type of {@code finalizee}
* @return a Finalizator used by GC to track given
* {@code finalizee} and invoke given {@code thunk} with it when the
* finalizee becomes unreachable
* @throws NullPointerException if either {@code finalizee} of {@code thunk}
* are null
*/
public static Finalizator create(T finalizee, Consumer super T> thunk) {
Objects.requireNonNull(finalizee);
Objects.requireNonNull(thunk);
int rnd = nextSecondarySeed();
int index = (rnd >>> 1) & (uncleaned.length - 1);
Finalizator finalizator = new Finalizator<>(finalizee, index, thunk);
uncleaned[index].link(finalizator, (rnd & 1) == 0);
return finalizator;
}
private Finalizator(T finalizee, int listIndex, Consumer super T> thunk) {
super(finalizee, listIndex);
this.thunk = thunk;
}
/**
* Invoked by GC when the tracked finalizee becomes unreachable or
* by user code at any time.
* It invokes the finalizator's thunk with the finalizee if this
* Finalizator has not been {@link #clear() cleared} before that. If invoked
* multiple times, only the 1st invocation results in the invocation
* of the thunk. The finalizator releases the reference to the thunk and
* finalizee upon 1st invocation of this method regardless of whether
* it was performed by GC or by user code.
*/
@Override
public void clean() {
super.clean();
}
/**
* Invoke the {@link #thunk} passing the {@code finalizee} to it.
*/
@Override
void invokeFinalizee(T finalizee) throws Throwable {
Consumer super T> thunk = this.thunk;
this.thunk = null;
thunk.accept(finalizee);
finalizee = null;
}
/**
* Returns {@code null} to prevent unwanted retention of the tracked
* {@code finalizee}.
*
* @return {@code null}
*/
@Override
public T get() {
return null;
}
/**
* Finalizator is not registered with a reference queue when created,
* so this method always returns {@code false}.
*
* @return {@code false}
*/
@Override
public boolean isEnqueued() {
return false;
}
/**
* Finalizator is not registered with a reference queue when created,
* so this method does nothing and always returns {@code false}.
*
* @return {@code false}
*/
@Override
public boolean enqueue() {
return false;
}
/**
* Clears this Finalizator and releases it's tracked {@code finalizee} and
* it's {@code thunk} if they have not been released yet.
* Invoking this method does not invoke the Finalizator's {@code thunk}.
* It prevents from {@link #clean() running} the {@code thunk} in the future
* if it has not been run yet.
*/
@Override
public void clear() {
super.clear();
}
}