/*
* 練習問題17.4 p.406
* 刈り取りスレッドを修正して、すべての割り当てられたリソースが解放されるまで、シャットダウンの後も
* 生き続けるようにしなさい。
*/
/*
* 練習問題17.3 p.406
* ハッシュコードを使用する代わりに、キーを管理することで参照オブジェクトを使用するように、
* リソース実装クラスを書き直しなさい。
*/
package ch17.ex17_04;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Map;
public class ResourceManager
{
final ReferenceQueue<Object> queue;
final Map<Reference<?>, Resource> refs;
final Thread reaper;
boolean shutdown = false;
public ResourceManager()
{
queue = new ReferenceQueue<Object>();
refs = new HashMap<Reference<?>, Resource>();
reaper = new ReaperThread();
reaper.start();
// ... リソースの初期化 ...
}
public synchronized void shutdown()
{
if (!shutdown)
{
shutdown = true;
reaper.interrupt();
}
}
public synchronized Resource getResource(Object key)
{
if (shutdown)
{
throw new IllegalStateException();
}
Resource res = new ResourceImpl(key);
Reference<?> ref = new PhantomReference<Object>(key, queue);
refs.put(ref, res);
return res;
}
private static class ResourceImpl implements Resource
{
// int keyHash;
SoftReference<Object> implKey;
boolean needsRelease = false;
ResourceImpl(Object key)
{
// keyHash = System.identityHashCode(key);
implKey = new SoftReference<Object>(key);
// .. 外部リソースの設定
needsRelease = true;
}
public void use(Object key, Object... args)
{
// if (System.identityHashCode(key) != keyHash)
if (!implKey.get().equals(key))
{
throw new IllegalArgumentException("wrong key");
}
// ... リソースの使用 ...
// System.out.println(key.toString() + " is used. ");
}
public synchronized void release()
{
if (needsRelease)
{
needsRelease = false;
// ... リソースの解放 ...
implKey.clear();
}
}
}
class ReaperThread extends Thread
{
public void run()
{
boolean threadShutdown = false;
// 割り込まれるまで実行
while(true)
{
try
{
Reference<?> ref = queue.remove();
Resource res = null;
synchronized(ResourceManager.this)
{
res = refs.get(ref);
refs.remove(ref);
}
res.release();
ref.clear();
}
catch (InterruptedException ex)
{
// break; // すべて終了
threadShutdown = true;
if (refs.size() == 0)
{
System.out.println("all resouces are released. ");
Runtime.getRuntime().gc();
System.out.println("Shutdown finished: " + Runtime.getRuntime().freeMemory());
return;
}
else
{
System.out.println("waiting all resources are released. ");
}
}
if (refs.size() == 0 && threadShutdown)
{
System.out.println("all resouces are released. ");
Runtime.getRuntime().gc();
System.out.println("Shutdown finished: " + Runtime.getRuntime().freeMemory());
return;
}
}
}
}
public static void main(String[] args)
{
Runtime.getRuntime().gc();
System.out.println("First memory state: " + Runtime.getRuntime().freeMemory());
ResourceManager test = new ResourceManager();
Resource[] resources = new Resource[10000];
for (int i = 0; i < resources.length; i++)
{
String tmp = new String("Sample resouce " + i);
resources[i] = test.getResource(tmp);
resources[i].use(tmp, (Object[])null);
}
System.out.println("10000 resources are used: " + Runtime.getRuntime().freeMemory());
for (int i = 0; i < ((resources.length) / 2); i++)
{
resources[i].release();
}
Runtime.getRuntime().gc();
System.out.println("Half resources are released: " + Runtime.getRuntime().freeMemory());
test.shutdown();
System.out.println("Shutdown ResourceManager");
try
{
Thread.sleep(100);
}
catch(Exception e)
{
System.out.println(e);
}
for(int i = ((resources.length) / 2); i < resources.length; i++)
{
resources[i].release();
}
Runtime.getRuntime().gc();
System.out.println("Rest resources are released: " + Runtime.getRuntime().freeMemory());
}
}