Комментарии 40
Рассматривали ли вы вариант AOP? Думаю, что для решения вашей задачи он очень хорошо подходит. Единственный момент — нужно будет использовать load-time weaving (так как 1) вызовы идут из скриптинга и 2) класс Thread относится к JRE). Советую почитать документацию к AspectJ — www.eclipse.org/aspectj/ довольно полезная штука.
+1
нет, не рассматривал. Было бы великолепно если бы смогли привести альтернативный вариант реализации с использованием AOP! будет прям два решения в одном топике.
0
Попробовал сделать с использованием AspectJ, но оказалось, что он не поддерживает weaving для классов из Bootclassloader-а. Возможно, у других реализаций AOP таких проблем нет, но я пока с ними не знаком. Попробую потом на свежую голову.
Из того, что сделать аспектами — добавить поле (или пару — для обратной связи ) в класс Thread и инициализировать его в конструкторе (изменение конструктора + «использование не threadQ, а добавленного поля» — переложить на AOP). Ну и использовать CFLOW чтобы отслеживать только то, что вызывается из вашего скриптинга и не трогать другие части программы.
Сорри, если сумбурно изложил свои мысли — в сон уже клонит.
Из того, что сделать аспектами — добавить поле (или пару — для обратной связи ) в класс Thread и инициализировать его в конструкторе (изменение конструктора + «использование не threadQ, а добавленного поля» — переложить на AOP). Ну и использовать CFLOW чтобы отслеживать только то, что вызывается из вашего скриптинга и не трогать другие части программы.
Сорри, если сумбурно изложил свои мысли — в сон уже клонит.
0
добавить поле не получится — redefineClasses будет ругаться, я в статье описал что: переопределение класса не должно добавлять, удалять новые поля или методы, менять сигнатуру методов или иерархию наследования, можно менять только тело методов, структура класса меняться не должна.
а можно поподробнее про CFLOW и контроль вызова классов в разных частях?
а можно поподробнее про CFLOW и контроль вызова классов в разных частях?
0
Этот механизм позволяет изменять код только для случаев, когда выполнение началось из определенной точки.
Например, мы имеем конфигурацию
В результате наш «перехватчик» будет вызван только при вызове init() из конструктора, но не при прямом вызове. То есть механизм, позволяющий смотреть на стек вызова.
Например, мы имеем конфигурацию
<cflow-stack name="mainConstructor"> <called expr="threadtree.Main->new(..)" /> </cflow-stack> <bind pointcut="execution(void threadtree.Main->init())" cflow="mainConstructor"> <interceptor class="threadtree.aspects.MyInterceptor" /> </bind>
public class Main { public Main() { init(); } public void init() { } public static void main(String[] args) throws Exception { Main m = new Main(); m.init(); } }
В результате наш «перехватчик» будет вызван только при вызове init() из конструктора, но не при прямом вызове. То есть механизм, позволяющий смотреть на стек вызова.
0
Согласен, javassist предоставляет низкоуровневые возможности для АОП, чем вы по-сути и пользуетесь в статье. Попытка решения с JBoss AOP тоже ни к чему не привела. Что и как там делалось я описал у себя в блоге «для истории» — можете взглянуть, если будет интересно (текста там немного, но в комментарий не уместилось).
0
А почему бы не сделать класс ManagedThread extends Thread c полем parentThread и конструкторами
ManagedThread(...) {
super(...);
this.parentThread = currentThread();
}
и далее разрешать из стороннего кода инстанциировать только его?
threadQ может в теле класса не использовать, но использоваться самой JVM в native коде
ManagedThread(...) {
super(...);
this.parentThread = currentThread();
}
и далее разрешать из стороннего кода инстанциировать только его?
threadQ может в теле класса не использовать, но использоваться самой JVM в native коде
+2
Да, имхо в этом случае не стоит этот самый threadQ трогать. Может, стоит воспользоваться каким-нить другим костылем, например каким-нить общим инстансом WeakHashMap?
0
хорошо заметили относительно threadQ — есть опасения про использование в native. Так ка информации я не нашел, придется проверять на «собственной шкуре».
в добавок не хотелось бы обрезать доступность класса стандартного Thread, так как на него завязано много других стандартных классов классов типа TimerThread и прочие.
если можно опишите детальнее как вы предполагали запретить инстанциирование стандартного Thread, возможно это пригодится в дальнейшем в других местах…
в добавок не хотелось бы обрезать доступность класса стандартного Thread, так как на него завязано много других стандартных классов классов типа TimerThread и прочие.
если можно опишите детальнее как вы предполагали запретить инстанциирование стандартного Thread, возможно это пригодится в дальнейшем в других местах…
0
Что касается ThreadTimer, то он спокойно примет в качестве аргумента ManagedThread т.к. он расширяет Thread. Сам класс будет доступен, надо лишь запретить вызывать new Thread() (и, возможно, несколько методов из java.util.concurrent)
Запрет инстациирования делается через security manager (вот например: www.javaworld.com/javaworld/jw-11-1997/jw-11-hood.html). Именно через него сделан запред инстациирования класса Thread в GoogleAppEngine (и во всех остальных application container'ах он тоже используется, если надо запрещать что-то инстанциировать или к чему-то обращатся).
Запрет инстациирования делается через security manager (вот например: www.javaworld.com/javaworld/jw-11-1997/jw-11-hood.html). Именно через него сделан запред инстациирования класса Thread в GoogleAppEngine (и во всех остальных application container'ах он тоже используется, если надо запрещать что-то инстанциировать или к чему-то обращатся).
0
как же TimerThread примет ManagedThread если у него по умолчанию нет для этого интерфейса?
При запрете вызова new Thread() — я запрещу вызывать конструктор new TimerThread(TaskQueue taskqueue).
Относительно запрета инстациирования спасибо, так и предполагалось, даже исходники гугля просмотрел :), думал может еще есть какие альтернативные варианты.
При запрете вызова new Thread() — я запрещу вызывать конструктор new TimerThread(TaskQueue taskqueue).
Относительно запрета инстациирования спасибо, так и предполагалось, даже исходники гугля просмотрел :), думал может еще есть какие альтернативные варианты.
0
ну т.е. запретив вызывать new Thread() — автоматически заблокируются все вызовы этого конструктора из всех стандартных классов, т.о. много стандартных классов будут нерабочими.
0
Ну, например все классы из java.util.concurrent опционально принимают ThreadFactory. Да, видимо классы типа Timer и прочие нужно будет тоже запретить.
Что, в принципе, нормально. Потому что я не могу представить ситуацию, когда какому-нибудь бизнес-приложению в application container'е на скриптовом языке надо активно оперерировать тредами.
Что, в принципе, нормально. Потому что я не могу представить ситуацию, когда какому-нибудь бизнес-приложению в application container'е на скриптовом языке надо активно оперерировать тредами.
0
Тоже сразу такая же мысль возникла. Пост читал с неким недоумением, хотя там и дисклеймер про бубен есть =)
0
А ведь можно по-другому.
Есть такой класс —
Есть такой класс —
InheritableThreadLocal
. У него есть метод childValue()
, который вызывается в родительской нити, до того как дочерняя нить стартует. Ясно, что таким образом можно сделать так, чтобы дети знали про своих родителей. PROFIT!!!+3
НЛО прилетело и опубликовало эту надпись здесь
примерчик приведите, не понял ход мысли…
0
Хотел как-то про это даже пост написать, но решил, что никому не интересно.
Очень рад, что могу с кем-то поделиться ноу-хау. Ловите:
Очень рад, что могу с кем-то поделиться ноу-хау. Ловите:
public class ThreadingTest {
private static InheritableThreadLocal<Thread> threadTracker = new InheritableThreadLocal<Thread>() {
@Override
protected Thread childValue(Thread parentValue) {
return Thread.currentThread();
}
};
public static void main(String[] args)
{
new Thread("parent")
{
@Override
public void run() {
System.out.println("parent: my parent is " + threadTracker.get() );
new Thread("child")
{
@Override
public void run() {
System.out.println("child: my parent is " + threadTracker.get() );
new Thread("grand_child")
{
@Override
public void run() {
System.out.println("grand_child: my parent is " + threadTracker.get() );
}
}.start();
}
}.start();
}
}.start();
}
}
* This source code was highlighted with Source Code Highlighter.
+5
Красиво?
0
решение хорошее, но не универсальное.
вопрос вот в чем, получается что я могу получить ссылку на поток-родитель только изнутри ребенка, а в моем конкретном случае надо иметь связи извне, т.е. контроль производит внешний класс, который перебирает потоки и хочет знать кто родитель каждого потока.
вопрос вот в чем, получается что я могу получить ссылку на поток-родитель только изнутри ребенка, а в моем конкретном случае надо иметь связи извне, т.е. контроль производит внешний класс, который перебирает потоки и хочет знать кто родитель каждого потока.
0
Это правда.
Вот что можно ещё сделать: сказать пользователям, что нити можно запускать только через какой-нибудь MyTrackingManager.createThread(). Ну фреймворк такой, лучше чем ничего.
А отлеживать, что пользователи не запустят нить откуда-либо ещё можно через этот самый childValue(), так как вызывается он из родительской нити (и у Ваc будет стек-трейс вызова).
Вот что можно ещё сделать: сказать пользователям, что нити можно запускать только через какой-нибудь MyTrackingManager.createThread(). Ну фреймворк такой, лучше чем ничего.
А отлеживать, что пользователи не запустят нить откуда-либо ещё можно через этот самый childValue(), так как вызывается он из родительской нити (и у Ваc будет стек-трейс вызова).
0
НЛО прилетело и опубликовало эту надпись здесь
запрет это крайняя мера присечения. проблемы всегда были и будут. были проблемы куда похлеще рассмотренной, но все они постепенно решились.
> Например, некоторые действия порожденной нити вызвали появление глобального пула объектов, пополняемого уже нитью этого пула. И эта нить будет включена в иерархию вместо того, что бы быть обособленной.
честно говоря недогнал суть проблемы, может примерчиком объясните?
> Например, некоторые действия порожденной нити вызвали появление глобального пула объектов, пополняемого уже нитью этого пула. И эта нить будет включена в иерархию вместо того, что бы быть обособленной.
честно говоря недогнал суть проблемы, может примерчиком объясните?
0
НЛО прилетело и опубликовало эту надпись здесь
да, но пул конектов к БД как раз таки всегда инициализируется до сервиса скриптинга, сами скрипты лежат в БД, т.е. запуск скрипа на выполнение уже предполагает наличие инициализированного пула. да и в целом система построена так, что пул конектов к БД всегда инициализируется раньше каких либо пользовательских скриптов.
Действительно, в системе, предполагается принудительное завершение дочерних потоков, которые не уложись в таймаут, все что породили пользовательские скрипты умрет после порога ожидания.
Возможно, выплывут какие-то дополнительные подводные камни, о которых пока не известно. Переключить галочку на запрет всегда успеется. Но, на данный момент на горизонте нет препятствий.
Действительно, в системе, предполагается принудительное завершение дочерних потоков, которые не уложись в таймаут, все что породили пользовательские скрипты умрет после порога ожидания.
Возможно, выплывут какие-то дополнительные подводные камни, о которых пока не известно. Переключить галочку на запрет всегда успеется. Но, на данный момент на горизонте нет препятствий.
0
НЛО прилетело и опубликовало эту надпись здесь
Насколько я понял исходная задача — иметь возможность привязать все потоки порожденные приложением к этому приложению.
Для этого проще всего использовать ThreadGroup.
Создайте корневой поток приложения в отдельном ThreadGroup. Все пороженные им потоки будут добавлены в этот ThreadGroup. Если же поток создаст свой ThreadGroup то он также будет добавлен в родительский ThreadGroup, и обходя дерево групп можно перечислить все потоки приложения, ну или обратная задача — имея конкретный поток сказать какой группе он принадлежит.
Для этого проще всего использовать ThreadGroup.
Создайте корневой поток приложения в отдельном ThreadGroup. Все пороженные им потоки будут добавлены в этот ThreadGroup. Если же поток создаст свой ThreadGroup то он также будет добавлен в родительский ThreadGroup, и обходя дерево групп можно перечислить все потоки приложения, ну или обратная задача — имея конкретный поток сказать какой группе он принадлежит.
+1
Нифига не понял.
Во-первых, вместо javaagent лучше исправить java.lang.Thread и запустить java с bootclasspath.
Во-вторых, если задача — контролировать код внутри jvm, нужно создать свой sandbox, написать для него свой ClassLoader, установить SecurityManager и выполнять код внутри на подобие как это делают аплеты.
Что касается тредов, то посмотрите внимательно конструктор java.sun.com/j2se/1.5.0/docs/api/java/lang/Thread.html#Thread(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String)
Там в явную идет security проверка на access к текущему threadGroup. При помощи этого можно контролировать возможность создания тредов.
Во-первых, вместо javaagent лучше исправить java.lang.Thread и запустить java с bootclasspath.
Во-вторых, если задача — контролировать код внутри jvm, нужно создать свой sandbox, написать для него свой ClassLoader, установить SecurityManager и выполнять код внутри на подобие как это делают аплеты.
Что касается тредов, то посмотрите внимательно конструктор java.sun.com/j2se/1.5.0/docs/api/java/lang/Thread.html#Thread(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String)
Там в явную идет security проверка на access к текущему threadGroup. При помощи этого можно контролировать возможность создания тредов.
0
1) чем лучше? думаю в случае в javaagent целостность решения лучше, все происходит в одном месте, приложение само под себя настраивает VM
2) а по другому наверно и нет возможности, только писать свой SecurityManager
задача стоит так: нужно не запретить, а иметь возможность держать контроль над потоками
2) а по другому наверно и нет возможности, только писать свой SecurityManager
задача стоит так: нужно не запретить, а иметь возможность держать контроль над потоками
0
спасибо еще раз за наводку на access к текущему threadGroup. Она пригодилась.
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
«Танцы с бубном» вокруг Thread