Вызов System.gc
ничего не делает, потому что нечего собирать. Когда поток запускается, он увеличивает счетчик ссылок на потоки, если это не будет сделано, поток будет прерываться неопределенно. Когда метод запуска потока завершается, счетчик ссылок потока уменьшается.
while (true) {
// just a simple demo, not useful code.
// 0 0 - the first number is thread reference count, the second is abq ref count
final ArrayBlockingQueue<Integer> abq = new ArrayBlockingQueue<Integer>(2);
// 0 1
final Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
abq.take();
// 2 2
} catch (final InterruptedException e) {
e.printStackTrace();
}
}
});
// 1 1
t.start();
// 2 2 (because the run calls abq.take)
// after end of loop
// 1 1 - each created object's reference count is decreased
}
Теперь существует потенциальное состояние гонки - что, если основной цикл завершается и выполняет сборку мусора до того, как поток t сможет выполнить какую-либо обработку, то есть он будет приостановлен ОС до выполнения оператора abq.take? Метод run попытается получить доступ к объекту abq после того, как GC выпустил его, что было бы плохо.
Чтобы избежать условия гонки, вы должны передать объект в качестве параметра методу выполнения. Я не уверен насчет Java в эти дни, это было давно, поэтому я бы предложил передать объект в качестве параметра конструктора классу, производному от Runnable
. Таким образом, перед вызовом метода run есть дополнительная ссылка на abq, что гарантирует, что объект всегда будет действительным.