Java.Как обратится к объекту, созданному в новом потоке?
создаю в новом потоке объект, но ссылку для него хочу так же иметь в главном потоке(что то вроде параллельной инициализации), я думал сделать переменную object как final, но компилятору этого недостаточно, он предлагает конструкцию типа final Object[] object = new Object[1] и потом в этот массив добавить ссылку object[0] = new Object() из нового потока. Чем его не устроила переменная объявленная как финал? Или такая практика параллельной работы не приветствуется вообще? а как же к примеру в Swing отделить графический интерфейс от общего потока с сохранением связи между ними?
public static void main(String[] args) {
Object object;
Thread t = new Thread(new Runnable() {
@Override
public void run() {
object = new Object();
}
};
t.start();
}
Ответы (2 шт):
Самый простой способ:
ArrayList<Object> objects = new ArrayList<>();
// Создаем поток
Thread t = new Thread(new Runnable() {
@Override
public void run() {
objects.add("new Object()");
}
});
// Запускаем поток
t.start();
// Ждем, пока он отработает
t.join();
System.out.println(objects);
P.S. Полезно для набивания самому себе шишек, с последующим исследованием. В этом примере не хватает как минимум потокобезопасной волатильной коллекции, если потоков будет много.
Требование модификатора final тут не связано с мультипоточностью. Все дело в том, что переменные объявленные в теле метода перестают существовать при выходе из метода. Обойдемся без мультипоточности, для примера анонимный класс:
Runnable deferredGreeting() {
String phrase = "is the answer";
int answer = 42;
return new Runnable() {
public void run() {
System.out.println(answer + " " + phrase);
}
};
}
При компиляции на старой версии java здесь будет ошибка, а компилятор java8 сам, не явно, проставит модификаторы final перед String phrase и int answer, все потому, что созданный Runnable вернется из метода и продолжит "жить", а локальные переменные уже перестанут существовать.
Runnable result = defferedGreepting(); // при вызове метода создались а затем уничтожились локальные переменные
result.run(); // А здесь будет запущена логика которая попытается сконструировать строку из локальных переменных.
Модификатор final гарантирует неизменность значения переменной, а значит в лямбду или, как в примере выше, в анонимный класс можно поместить копии значений. Т.е. захвата переменных не происходит, происходит захват значений, для строки значением будет указатель на строку, а для int само значение 42.
final Object[] holder = new Object[1];
Это одно из возможных решений, holder содержит ссылку на массив аллоцированный в куче. Копия ссылки создается в анонимном классе, таким образом на массив будет существовать две ссылки, одна из переменной в методе, вторая из тела анонимного класса new Runnable(), когда произойдет выход из метода, первая ссылка будет уничтожена, но останется вторая и тогда GC не удалит массив new Object[1]. В свою очередь массив содержит ячейку в которую можно записать ссылку на результат.
Другое возможно решение использовать AtomicReference
public static void main(String ... args) {
final AtomicReference<String> result = new AtomicReference<>();
Thread t = new Thread(new Runnable() {
@Override
public void run() {
result.set("Hello");
}
});
// ...
}
Обычно теперь так не пишут, потому как дальше возникнут проблемы с доступом к разделяемому ресурсу, вот как ты узнаешь что результат готов? В какой момент можно прочитать значение? Но это уже совсем другая история.