понедельник, 24 марта 2008 г.

Java Puzzle 78: Reflection Infection

Рассмотрим простое применение refletion. Что напечатает следующая программа?
import java.lang.reflection.*;
import java.util.*;
public class Reflector {
public static void main(String[] args)
throws Exception {
Set set = new HashSet();
set.add("foo");
Iterator it = set.iterator();
Class type = it.getClass();
Method method = type.getMethod("hasNext");
System.out.println(method.invoke(it));
}
}

Нашли, блин, чем меня удивить! Я с такими проблемами в JavaBeans регулярно сталкиваюсь. Несмотря на то, что метод определён как public, он определён в private классе. И у пользователя просто нет прав, чтобы его вызвать. Приходится рекурсивно проходить по иерархии классов и интерфейсов и искать его пототип, который действительно разрешён к использованию. Обратите внимание, что еcли метод получить другим образом, то он будет работать:
    Method method = Iterator.class.getMethod("hasNext");

Java Puzzle 77: The Lock Mess Monster

Разберитесь-ка, как работает следующая программа:
import java.util.*;
public class Worker extends Thread {
public static void main(String[] args)
throws InterruptedException {
final Worker worker = new Worker();
worker.start();
Timer timer = new Timer(true);
timer.schedule(
new TimerTask() {
public void run() {
worker.keepWorking();
}
}, 500);
sleep(400);
worker.quit();
}
private volatile boolean stop = false;
public void run() {
while (!stop) pretendToWork();
System.out.println("Beer is good!");
}
private void pretendToWork() {
try {
sleep(300);
}
catch (InterruptedException exception) {
}
}
synchronized void keepWorking() {
stop = false;
}
synchronized void quit() {
stop = true;
join();
}
}

Беглый анализ показывает, что где-то через секунду программа напишет Beer is good! и завершит свою работу. Но на самом деле она зависает! Проблема в том, что внутренняя реализация метода join вызывает метод wait на экземпляре класса Thread, который должен быть присоединён. А вызов метода wait снимает блокировку!
Фундаментальная причина ошибочного поведения класса Worker - использование блокировки по экземпляру (synchronized методы). Не полагайтесь на поведение библиотеки - делайте собственные объекты блокировки, которые не доступны никому другому. Это напоминает раздел 15 книги Эффективное программирование Джошуа Блоха.