Учтите, что потоки-демоны завершают свои методы run() без выполнения секций finally:
//: concurrency/DaemonsDontRunFinally.java
// Потоки-демоны не выполняют секцию finally.
import java.util.concurrent.*.
import static net.mindview.util.Print.*,
class ADaemon implements Runnable { public void run() { try {
print("Запускаем ADaemon"); TimeUnit.SECONDS.sieep(l). } catch(InterruptedException e) {
print("Выход через InterruptedException"); } finally {
print("Должно выполняться всегда?");
}
}
}
public class DaemonsDontRunFinally {
public static void main(String[] args) throws Exception { Thread t = new Thread(new ADaemonO); t.setDaemon(true). t.startO,
}
} /* Output;
Запускаем ADaemon
*///:-
Запуск программы наглядно показывает, что секция finally не выполняется. С другой стороны, если закомментировать вызов setDaemon(), вы увидите, что секция finally была выполнена.
Такое поведение верно, даже если из предыдущих описаний finally у вас сложилось обратное впечатление. Демоны завершаются «внезапно», при завершении последнего не-демона. Таким образом, сразу же при выходе из main() JVM немедленно прерывает работу всех демонов, не соблюдая никакие формальности. Невозможность корректного завершения демонов ограничивает возможности их применения. Обычно объекты Executor оказываются более удачным решением, потому что все задачи, находящиеся под управлением Executor, могут быть завершены одновременно.
Варианты кодирования
Во всех предшествующих примерах все классы задач реализовали интерфейс Runnable. В очень простых случаях можно использовать альтернативное решение с прямым наследованием от Thread:
//• concurrency/SimpleThread.java // Прямое наследование от класса Thread.
public class SimpleThread extends Thread { private int countDown = 5; private static int threadCount = 0. public SimpleThreadO {
// Сохранение имени потока
super(Integer.toStri ng(++threadCount)).
startO.
}
public String toStringO {
return "#" + getNameO + "(" + countDown + "), ";
}
public void run() {
while(true) {
System out print(this). if(--countDown == 0) return,
}
}
public static void main(String[] args) { for(int i = 0, i < 5, i++) new SimpleThreadO.
}
} /* Output
#1(5). #1(4). #1(3). #1(2). #1(1). #2(5). #2(4). #2(3). #2(2). #2(1). #3(5). #3(4). #3(3). #3(2). #3(1). #4(5). #4(4), #4(3). #4(2). #4(1). #5(5). #5(4). #5(3). #5(2). #5(1).
Чтобы задать объектам Thread имена, вы вызываете соответствующий конструктор Thread. Имя читается в методе toStringO при помощи getName().
Также иногда встречается идиома самоуправляемой реализации Runnable:
// concurrency/SelfManaged.java
// Реализация Runnable. содержащая собственый объект Thread
public class SelfManaged implements Runnable { private int countDown = 5. private Thread t = new Thread(this). public SelfManagedO { t startO. } public String toStringO {
return Thread currentThreadO .getNameO + "(" + countDown + "). ";
}
public void run() {
while(true) {
System out print(this). if(--countDown == 0) return.
}
}
public static void main(Stnng[] args) { for(int i = 0. i < 5. i++) new SelfManagedO,