下面列出了怎么用java.lang.ref.Cleaner的API类实例代码及写法,或者点击链接到github查看源代码。
/**
* Starts the Cleaner implementation.
* Ensure this is the CleanerImpl for the Cleaner.
* When started waits for Cleanables to be queued.
* @param cleaner the cleaner
* @param threadFactory the thread factory
*/
public void start(Cleaner cleaner, ThreadFactory threadFactory) {
if (getCleanerImpl(cleaner) != this) {
throw new AssertionError("wrong cleaner");
}
// schedule a nop cleaning action for the cleaner, so the associated thread
// will continue to run at least until the cleaner is reclaimable.
new CleanerCleanable(cleaner);
if (threadFactory == null) {
threadFactory = CleanerImpl.InnocuousThreadFactory.factory();
}
// now that there's at least one cleaning action, for the cleaner,
// we can start the associated thread, which runs until
// all cleaning actions have been run.
Thread thread = threadFactory.newThread(this);
thread.setDaemon(true);
thread.start();
}
/**
* Starts the Cleaner implementation.
* Ensure this is the CleanerImpl for the Cleaner.
* When started waits for Cleanables to be queued.
* @param cleaner the cleaner
* @param threadFactory the thread factory
*/
public void start(Cleaner cleaner, ThreadFactory threadFactory) {
if (getCleanerImpl(cleaner) != this) {
throw new AssertionError("wrong cleaner");
}
// schedule a nop cleaning action for the cleaner, so the associated thread
// will continue to run at least until the cleaner is reclaimable.
new CleanerCleanable(cleaner);
if (threadFactory == null) {
threadFactory = CleanerImpl.InnocuousThreadFactory.factory();
}
// now that there's at least one cleaning action, for the cleaner,
// we can start the associated thread, which runs until
// all cleaning actions have been run.
Thread thread = threadFactory.newThread(this);
thread.setDaemon(true);
thread.start();
}
/**
* Test that sequences of the various actions on a Reference
* and on the Cleanable instance have the desired result.
* The test cases are generated for each of phantom, weak and soft
* references.
* The sequence of actions includes all permutations to an initial
* list of actions including clearing the ref and resulting garbage
* collection actions on the reference and explicitly performing
* the cleaning action.
*/
@Test
@SuppressWarnings("unchecked")
void testCleanableActions() {
Cleaner cleaner = Cleaner.create();
// Individually
generateCases(cleaner, c -> c.clearRef());
generateCases(cleaner, c -> c.doClean());
// Pairs
generateCases(cleaner, c -> c.doClean(), c -> c.clearRef());
CleanableCase s = setupPhantom(COMMON, cleaner);
cleaner = null;
checkCleaned(s.getSemaphore(), true, "Cleaner was cleaned:");
}
/**
* Test the jdk.internal.misc APIs with sequences of the various actions
* on a Reference and on the Cleanable instance have the desired result.
* The test cases are generated for each of phantom, weak and soft
* references.
* The sequence of actions includes all permutations to an initial
* list of actions including clearing the ref and resulting garbage
* collection actions on the reference, explicitly performing
* the cleanup and explicitly clearing the cleaning action.
*/
@Test
@SuppressWarnings("unchecked")
void testRefSubtypes() {
Cleaner cleaner = Cleaner.create();
// Individually
generateCasesInternal(cleaner, c -> c.clearRef());
generateCasesInternal(cleaner, c -> c.doClean());
generateCasesInternal(cleaner, c -> c.doClear());
// Pairs
generateCasesInternal(cleaner,
c -> c.doClear(), c -> c.doClean());
// Triplets
generateCasesInternal(cleaner,
c -> c.doClear(), c -> c.doClean(), c -> c.clearRef());
generateExceptionCasesInternal(cleaner);
CleanableCase s = setupPhantom(COMMON, cleaner);
cleaner = null;
checkCleaned(s.getSemaphore(), true, "Cleaner was cleaned:");
}
/**
* Test that releasing the reference to the Cleaner service allows it to be
* be freed.
*/
@Test
void testCleanerTermination() {
ReferenceQueue<Object> queue = new ReferenceQueue<>();
Cleaner service = Cleaner.create();
PhantomReference<Object> ref = new PhantomReference<>(service, queue);
System.gc();
// Clear the Reference to the cleaning service and force a gc.
service = null;
System.gc();
try {
Reference<?> r = queue.remove(1000L);
Assert.assertNotNull(r, "queue.remove timeout,");
Assert.assertEquals(r, ref, "Wrong Reference dequeued");
} catch (InterruptedException ie) {
System.out.printf("queue.remove Interrupted%n");
}
}
/**
* Create a CleanableCase for a PhantomReference.
* @param cleaner the cleaner to use
* @param obj an object or null to create a new Object
* @return a new CleanableCase preset with the object, cleanup, and semaphore
*/
static CleanableCase setupPhantomSubclassException(Cleaner cleaner, Object obj) {
if (obj == null) {
obj = new Object();
}
Semaphore s1 = new Semaphore(0);
Cleaner.Cleanable c1 = new PhantomCleanable<Object>(obj, cleaner) {
protected void performCleanup() {
s1.release();
throw new RuntimeException("Exception thrown to cleaner thread");
}
};
return new CleanableCase(new PhantomReference<>(obj, null), c1, s1, true);
}
/**
* Create a CleanableCase for a WeakReference.
* @param cleaner the cleaner to use
* @param obj an object or null to create a new Object
* @return a new CleanableCase preset with the object, cleanup, and semaphore
*/
static CleanableCase setupWeakSubclassException(Cleaner cleaner, Object obj) {
if (obj == null) {
obj = new Object();
}
Semaphore s1 = new Semaphore(0);
Cleaner.Cleanable c1 = new WeakCleanable<Object>(obj, cleaner) {
protected void performCleanup() {
s1.release();
throw new RuntimeException("Exception thrown to cleaner thread");
}
};
return new CleanableCase(new WeakReference<>(obj, null), c1, s1, true);
}
/**
* Create a CleanableCase for a SoftReference.
* @param cleaner the cleaner to use
* @param obj an object or null to create a new Object
* @return a new CleanableCase preset with the object, cleanup, and semaphore
*/
static CleanableCase setupSoftSubclassException(Cleaner cleaner, Object obj) {
if (obj == null) {
obj = new Object();
}
Semaphore s1 = new Semaphore(0);
Cleaner.Cleanable c1 = new SoftCleanable<Object>(obj, cleaner) {
protected void performCleanup() {
s1.release();
throw new RuntimeException("Exception thrown to cleaner thread");
}
};
return new CleanableCase(new SoftReference<>(obj, null), c1, s1, true);
}
/**
* Called by Cleaner static initialization to provide the function
* to map from Cleaner to CleanerImpl.
* @param access a function to map from Cleaner to CleanerImpl
*/
public static void setCleanerImplAccess(Function<Cleaner, CleanerImpl> access) {
if (cleanerImplAccess == null) {
cleanerImplAccess = access;
} else {
throw new InternalError("cleanerImplAccess");
}
}
/**
* Constructs new {@code PhantomCleanable} with
* {@code non-null referent} and {@code non-null cleaner}.
* The {@code cleaner} is not retained; it is only used to
* register the newly constructed {@link Cleaner.Cleanable Cleanable}.
*
* @param referent the referent to track
* @param cleaner the {@code Cleaner} to register with
*/
public PhantomCleanable(T referent, Cleaner cleaner) {
super(Objects.requireNonNull(referent), CleanerImpl.getCleanerImpl(cleaner).queue);
this.list = CleanerImpl.getCleanerImpl(cleaner).phantomCleanableList;
insert();
// Ensure referent and cleaner remain accessible
Reference.reachabilityFence(referent);
Reference.reachabilityFence(cleaner);
}
/**
* Constructs new {@code WeakCleanableReference} with
* {@code non-null referent} and {@code non-null cleaner}.
* The {@code cleaner} is not retained by this reference; it is only used
* to register the newly constructed {@link Cleaner.Cleanable Cleanable}.
*
* @param referent the referent to track
* @param cleaner the {@code Cleaner} to register new reference with
*/
public WeakCleanable(T referent, Cleaner cleaner) {
super(Objects.requireNonNull(referent), CleanerImpl.getCleanerImpl(cleaner).queue);
list = CleanerImpl.getCleanerImpl(cleaner).weakCleanableList;
insert();
// Ensure referent and cleaner remain accessible
Reference.reachabilityFence(referent);
Reference.reachabilityFence(cleaner);
}
/**
* Constructs new {@code SoftCleanableReference} with
* {@code non-null referent} and {@code non-null cleaner}.
* The {@code cleaner} is not retained by this reference; it is only used
* to register the newly constructed {@link Cleaner.Cleanable Cleanable}.
*
* @param referent the referent to track
* @param cleaner the {@code Cleaner} to register with
*/
public SoftCleanable(T referent, Cleaner cleaner) {
super(Objects.requireNonNull(referent), CleanerImpl.getCleanerImpl(cleaner).queue);
list = CleanerImpl.getCleanerImpl(cleaner).softCleanableList;
insert();
// Ensure referent and cleaner remain accessible
Reference.reachabilityFence(referent);
Reference.reachabilityFence(cleaner);
}
/**
* Called by Cleaner static initialization to provide the function
* to map from Cleaner to CleanerImpl.
* @param access a function to map from Cleaner to CleanerImpl
*/
public static void setCleanerImplAccess(Function<Cleaner, CleanerImpl> access) {
if (cleanerImplAccess == null) {
cleanerImplAccess = access;
} else {
throw new InternalError("cleanerImplAccess");
}
}
/**
* Constructs new {@code PhantomCleanable} with
* {@code non-null referent} and {@code non-null cleaner}.
* The {@code cleaner} is not retained; it is only used to
* register the newly constructed {@link Cleaner.Cleanable Cleanable}.
*
* @param referent the referent to track
* @param cleaner the {@code Cleaner} to register with
*/
public PhantomCleanable(T referent, Cleaner cleaner) {
super(Objects.requireNonNull(referent), CleanerImpl.getCleanerImpl(cleaner).queue);
this.list = CleanerImpl.getCleanerImpl(cleaner).phantomCleanableList;
insert();
// Ensure referent and cleaner remain accessible
Reference.reachabilityFence(referent);
Reference.reachabilityFence(cleaner);
}
/**
* Constructs new {@code WeakCleanableReference} with
* {@code non-null referent} and {@code non-null cleaner}.
* The {@code cleaner} is not retained by this reference; it is only used
* to register the newly constructed {@link Cleaner.Cleanable Cleanable}.
*
* @param referent the referent to track
* @param cleaner the {@code Cleaner} to register new reference with
*/
public WeakCleanable(T referent, Cleaner cleaner) {
super(Objects.requireNonNull(referent), CleanerImpl.getCleanerImpl(cleaner).queue);
list = CleanerImpl.getCleanerImpl(cleaner).weakCleanableList;
insert();
// Ensure referent and cleaner remain accessible
Reference.reachabilityFence(referent);
Reference.reachabilityFence(cleaner);
}
/**
* Constructs new {@code SoftCleanableReference} with
* {@code non-null referent} and {@code non-null cleaner}.
* The {@code cleaner} is not retained by this reference; it is only used
* to register the newly constructed {@link Cleaner.Cleanable Cleanable}.
*
* @param referent the referent to track
* @param cleaner the {@code Cleaner} to register with
*/
public SoftCleanable(T referent, Cleaner cleaner) {
super(Objects.requireNonNull(referent), CleanerImpl.getCleanerImpl(cleaner).queue);
list = CleanerImpl.getCleanerImpl(cleaner).softCleanableList;
insert();
// Ensure referent and cleaner remain accessible
Reference.reachabilityFence(referent);
Reference.reachabilityFence(cleaner);
}
@SuppressWarnings("unchecked")
void generateCasesInternal(Cleaner cleaner, Consumer<CleanableCase>... runnables) {
generateCases(() -> setupPhantomSubclass(cleaner, null),
runnables.length, runnables);
generateCases(() -> setupWeakSubclass(cleaner, null),
runnables.length, runnables);
generateCases(() -> setupSoftSubclass(cleaner, null),
runnables.length, runnables);
}
@SuppressWarnings("unchecked")
void generateExceptionCasesInternal(Cleaner cleaner) {
generateCases(() -> setupPhantomSubclassException(cleaner, null),
1, c -> c.clearRef());
generateCases(() -> setupWeakSubclassException(cleaner, null),
1, c -> c.clearRef());
generateCases(() -> setupSoftSubclassException(cleaner, null),
1, c -> c.clearRef());
}
/**
* Create a CleanableCase for a PhantomReference.
* @param cleaner the cleaner to use
* @param obj an object or null to create a new Object
* @return a new CleanableCase preset with the object, cleanup, and semaphore
*/
static CleanableCase setupPhantom(Cleaner cleaner, Object obj) {
if (obj == null) {
obj = new Object();
}
Semaphore s1 = new Semaphore(0);
Cleaner.Cleanable c1 = cleaner.register(obj, () -> s1.release());
return new CleanableCase(new PhantomReference<>(obj, null), c1, s1);
}
/**
* Create a CleanableCase for a PhantomReference.
* @param cleaner the cleaner to use
* @param obj an object or null to create a new Object
* @return a new CleanableCase preset with the object, cleanup, and semaphore
*/
static CleanableCase setupPhantomSubclass(Cleaner cleaner, Object obj) {
if (obj == null) {
obj = new Object();
}
Semaphore s1 = new Semaphore(0);
Cleaner.Cleanable c1 = new PhantomCleanable<Object>(obj, cleaner) {
protected void performCleanup() {
s1.release();
}
};
return new CleanableCase(new PhantomReference<>(obj, null), c1, s1);
}
/**
* Create a CleanableCase for a WeakReference.
* @param cleaner the cleaner to use
* @param obj an object or null to create a new Object
* @return a new CleanableCase preset with the object, cleanup, and semaphore
*/
static CleanableCase setupWeakSubclass(Cleaner cleaner, Object obj) {
if (obj == null) {
obj = new Object();
}
Semaphore s1 = new Semaphore(0);
Cleaner.Cleanable c1 = new WeakCleanable<Object>(obj, cleaner) {
protected void performCleanup() {
s1.release();
}
};
return new CleanableCase(new WeakReference<>(obj, null), c1, s1);
}
/**
* Create a CleanableCase for a SoftReference.
* @param cleaner the cleaner to use
* @param obj an object or null to create a new Object
* @return a new CleanableCase preset with the object, cleanup, and semaphore
*/
static CleanableCase setupSoftSubclass(Cleaner cleaner, Object obj) {
if (obj == null) {
obj = new Object();
}
Semaphore s1 = new Semaphore(0);
Cleaner.Cleanable c1 = new SoftCleanable<Object>(obj, cleaner) {
protected void performCleanup() {
s1.release();
}
};
return new CleanableCase(new SoftReference<>(obj, null), c1, s1);
}
CleanableCase(Reference<Object> ref, Cleaner.Cleanable cleanup,
Semaphore semaphore) {
this.ref = ref;
this.cleanup = cleanup;
this.semaphore = semaphore;
this.throwsEx = false;
this.events = new int[4];
this.eventNdx = 0;
}
CleanableCase(Reference<Object> ref, Cleaner.Cleanable cleanup,
Semaphore semaphore,
boolean throwsEx) {
this.ref = ref;
this.cleanup = cleanup;
this.semaphore = semaphore;
this.throwsEx = throwsEx;
this.events = new int[4];
this.eventNdx = 0;
}
/**
* Example using a Cleaner to remove WeakKey references from a Map.
*/
@Test
void testWeakKey() {
ConcurrentHashMap<WeakKey<String>, String> map = new ConcurrentHashMap<>();
Cleaner cleaner = Cleaner.create();
String key = new String("foo"); // ensure it is not interned
String data = "bar";
map.put(new WeakKey<>(key, cleaner, map), data);
WeakKey<String> k2 = new WeakKey<>(key, cleaner, map);
Assert.assertEquals(map.get(k2), data, "value should be found in the map");
key = null;
System.gc();
Assert.assertNotEquals(map.get(k2), data, "value should not be found in the map");
final long CYCLE_MAX = Utils.adjustTimeout(30L);
for (int i = 1; map.size() > 0 && i < CYCLE_MAX; i++) {
map.forEach( (k, v) -> System.out.printf(" k: %s, v: %s%n", k, v));
try {
Thread.sleep(10L);
} catch (InterruptedException ie) {}
}
Assert.assertEquals(map.size(), 0, "Expected map to be empty;");
cleaner = null;
}
public WeakKey(K key, Cleaner c, ConcurrentHashMap<WeakKey<K>, ?> map) {
super(key);
this.hash = key.hashCode();
this.map = map;
cleanable = new WeakCleanable<Object>(key, c) {
protected void performCleanup() {
map.remove(WeakKey.this);
}
};
}
/**
* Test the Cleaner from the CleanerFactory.
*/
@Test
void testCleanerFactory() {
Cleaner cleaner = CleanerFactory.cleaner();
Object obj = new Object();
CleanableCase s = setupPhantom(cleaner, obj);
obj = null;
checkCleaned(s.getSemaphore(), true,
"Object was cleaned using CleanerFactor.cleaner():");
}
@Nonnull
@CheckReturnValue
@Override
public Cleaner cleaner() {
return CLEANER.get();
}
CleanerCleanable(Cleaner cleaner) {
super(cleaner, cleaner);
}
CleanerCleanable(Cleaner cleaner) {
super(cleaner, cleaner);
}