| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Java: Общие вопросы > Threads & lists |
| Автор: Sleepy_PIP 20.11.2004, 21:49 |
| Программа построена так, что хранит активные тхреады в нескольких LinkedList. При старте тхреада он заносится в несколько листов. При окончании run по finally тхред убирается из листа. У меня получается так - активных тхреадов скажем ~20, - уничтоженных (закончивших свои run и выполнивших finally с уничтожением ссылки на класс из всех листов) еще ~30. так в win32 по taskmanager вполне долгое время (2-5мин) существует ~50 тхреадов. такие дела. |
| Автор: Domestic Cat 20.11.2004, 22:02 |
| А зачем так много тредов то? |
| Автор: Sleepy_PIP 20.11.2004, 22:10 | ||
да я тут уже надоел навеное своей задачкой. Задачка - сканить N сайтов M тхреадами по FTP. с записъю а БД. посему надо много. А если учесть что каждый FTP коннект - как имнимум 2 тхреада (не моих) + минимум 1 тхерад на коннект к БД ... да еще каждый тхреад имеет как минимум 1 контрольный (на каждый сайт - 1 контрольный, и M сканящих - как минимум) ... ну и листов - по контрольным, по общим, и в нутри каждого контрольного по сканащим ... ну вообщем много получается. За одно проверяю разные фиреволы. оказывается далеко не все способны держать по 20-50 коннектов наружу |
| Автор: Domestic Cat 20.11.2004, 22:16 |
| Поставь им всем маленький приоритет (1), мзохно просмотреть сколько все это памяти ест и сразу столько давать (-Хms, -Хmn). Но вообще-то если тред был запущен через start() и выполнил свой run(), он должен убираться коллектором, в данной ситуации почти сразу . |
| Автор: Sleepy_PIP 20.11.2004, 22:17 | ||||||
но _моих_ тхреадов - не более 20 (5х4) - так они освобождаются нормально (по логам), тем не менее колл-во общих тхреадов далеко не следует за колл-вом моих тхреадов. задержка в 1-5 мин. не смотря на то что из всех листов все почищенно сразу. конечно все четко проверить и посчитать трудно. руководствуюсь тем, что утечки тхреадов нет, т.к. общее колл-во тхреадов таки возвращяется к некоей величине постоянно. Добавлено @ 22:21
у меня по эксплуатации складывается впечатление что если тхред в каком-то листе пофиксен - то пока из лита не уберешь - не освобождается. хотя run давно отработал. И даже более - как тольбко тхред который объявлен как public class GetListThr extends Thread создается конструктором - он уже _есть_ не важно - run он или нет. собственно это соответствует книжкам. Так вот - пока этот класс есть хоть в каком-то листе - он _будет_ и _будет_ занимать тхреад ... все под win32 конечно ... |
| Автор: Domestic Cat 20.11.2004, 22:22 |
| На самом деле даже если ты вообще о тредах не знаешь и пишешь 1 класс с 1 методом main, JVM пускает кучу своих тредов - диспетчер, коллектор и пр и пр. |
| Автор: Sleepy_PIP 20.11.2004, 22:23 | ||||||||
Еще далее - если класс тхреада есть в листе - он еще долго существует. Я подозреваю что листы чистятся довольно долго в отл. от обычных переменных по gc .... имхо - только подозрения. |
| Автор: Domestic Cat 20.11.2004, 22:24 | ||
само собой, ссылок на него не должно быть |
| Автор: Sleepy_PIP 20.11.2004, 22:26 | ||||||
их колл-во по сравнению с моими довольно мало. я заранее заню сколько даже побочных тхреадов на 1 мой скан. посему и вопию, что далеко не все сразу освобождается, что в листах. Кстати - без листов - все мгновенно (я не успеваю отследить). Добавлено @ 22:28
так дело то в том, что освобождаю я листы (в смысле удаляю ссылки на классы тхреадов из них) - но реакция следует через 1-5 мин. вот я об чем! и gc явный не помогает! |
| Автор: Domestic Cat 20.11.2004, 22:28 | ||||||
не класс, а ссылка на объект
чистка листа - относительно долгая процедура по сравнению с массивом, но не настолько же. Кстати, гц листы вообще не чистит, он убирает объекты на которые нет ссылок. Добавлено @ 22:29
а можно тут поподробнее? |
| Автор: Sleepy_PIP 20.11.2004, 22:32 | ||||||||||||||||||||||||||
ага. а если в листе есть ссылка на класс тхреада? тогда как? - лист .clear не выполнял. только .remove класса тхреада. Однкао тхреад остался. на некоторое время. Вот что у меня получается. Добавлено @ 22:33
ну нет, конечно - на экземпляр класса, т.е. на переменную типа класса тхреада ... т.е. ссылка. Добавлено @ 22:37
из main делаю цикл по созданию тхреада (экземпляра типа класса тхреада), жду когда он закончится (пишу его в лок. переменную и анализирую isAlive), после цикла - тхреадов - 0. моих |
| Автор: Domestic Cat 20.11.2004, 22:39 |
| Самое оптимальное решение для подобных задач - создать пул (pool) тредов, то есть к-л коллекшн с заранее запущенными 30-40 тредами. Треды эти спят все время, пока ты их не "будишь" для выпонения определенной задачи, поскле этого они возвращаются в пул. это будет намного эффективнее, т.к. не нужно будет непрерывно создавать/уничтожать их. Прочитать можно тут: http://www-106.ibm.com/developerworks/java/library/j-jtp0730.html http://www.informit.com/articles/article.asp?p=30483&redir=1 http://www.javaworld.com/javaworld/jw-05-1999/jw-05-toolbox.html http://www.samspublishing.com/articles/article.asp?p=30483&seqNum=3 |
| Автор: Sleepy_PIP 20.11.2004, 22:46 | ||
эт' я понимаю Если не так - прошу научить как сделать быстрее, при учете что в личтах - тхреады. |
| Автор: Domestic Cat 21.11.2004, 01:52 | ||||
Как раз той, тредов то много.
ГЦ из листов ничего удалять не может. Удаляешь ты (через ремув). А можно код где удаление треда из листа проишодит, посмотерть? |
| Автор: Sleepy_PIP 21.11.2004, 19:43 | ||||||
только прошу ногами не бить - я только учусь. Вот один из классов тхреадов (у меня их несколько под разные нужды). точнее его метод run //============================================================================= public void run() { try { llog.LogLogThr(getName(),"Site:"+ftps.URL+" "+"================================ Start scan list thread for site:"+ftps.URL, 4); // do scan site and write in DB dbc = dbw.ConnectDB(); if(dbc==null) { llog.LogLogThr(getName(),"Site:"+ftps.URL+" "+"Dont possible get DBConnection. Thread stop",0); return; } DoListFilesDB(lftp); return; }catch(Exception e) { llog.LogLogThrCallStack(getName(),"Site:"+ftps.URL+" "+"ERROR on scan dir, exception:"+e.toString(),0,e); } finally { try { if (dbc != null) { dbw.FreeConnection(dbc); dbc=null; } }catch(Exception e) { llog.LogLogThrCallStack(getName(),"Site:"+ftps.URL+" "+"ERROR on free connection, exception:"+e.toString(),0,e); dbc=null; } if(lftp!=null) { try { lftp.disconnect(); lftp=null; } catch(Exception e) { //!!! } lftp=null; } synchronized (PIPFTPSGetList.Gconf.GlobThreadList) { PIPFTPSGetList.Gconf.GlobThreadList.remove(this); } llog.LogLogThr(getName(),"Site:"+ftps.URL+" "+"================================ End scan list thread for site:" +ftps.URL, 4); } } как видно - уделения их листа вроде-бы не избежать. синхронизация - такая во всеэ экземплярах тхреада. Однако! запись в логе я вижу, а общее колл-во тхреадов по тасклисту не уменьшается так-же быстро, как появляется запись в логе. Речь идет о десятках тхреадов, так что разница весьма заметна. Прошу заметить заодно что в самом этом тхреаде используются как минимум еще создание 3-х тхреадов - 1 на коннкт к ДБ, и 2 под FTP - это уже не мои тхреады. - библиотечные. Тем не менее общее колл-во тхреадов может значительно превышать расчетное. на 10-20 к примеру в пике. не смотря на удаление и освобождение переменных ... |
| Автор: Domestic Cat 22.11.2004, 19:33 | ||
я посмотрел аналогичный код со 100 тредами:
Ничего плохого нe происходит - изнача льно есть 8 тредоv JVM, 1 main тред, затем их число растет до 109, и череz секунду падает дo 9. Так что проблема в чем-тo другом. Например, у тебя все синхронизировано нa листe. Если какой-тo другой тред (не тот для которогo тy постил коd) "смотрит" лист в синхронизированном блокe и достаточо часто, тo возможно что он тем самым блокирует доступ к листу другим треда, и они ждут момента kогда смогут себя удалить. Может, еще чт о. А зачем вообще тебе лист? |
| Автор: Sleepy_PIP 22.11.2004, 21:08 |
| да, подобные примеры я то-ж гонял, пытаясь понять. может и правда дело в другом, но уже все излазил вдоль и поперек. Самое интересное что колл-во тхреадов все-ж падает изредка до расчетного ... Зачем листы? - я с их помощъю контролю тхреды и просто банально собираю статистику по коллв-у тхреадов. А как еще узнать сколько все собственных тхреадов в _данный_ момент? - оно-ж плавает здорово ... на гдоб. переменных все делать?. но тогда я не смогу обращаться к классным переменным конкр. тхреада ... |
| Автор: Domestic Cat 22.11.2004, 21:16 |
| http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ThreadGroup.html Создай свой ThreadGroup, и добавляй в него треды, указывая ThreadGroup в конструкторе: http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Thread.html#Thread(java.lang.ThreadGroup,%20java.lang.Runnable) Тогда узнать количество тредов можно через activeCount(): http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ThreadGroup.html#activeCount() |
| Автор: igon 23.11.2004, 02:40 |
| Идея такая (возможно, бесполезная - на практике не проверял): Так как причина проблемы, тебя беспокоящей, в наличии неубранных из листов "мертвых" ссылок, хранить в листах не ссылки, а ИМЕНА тредов! 1.Каждый тред обзывешь уникально (setName("Имя")), имя фиксируешь в листе 2.Если нужно обратиться к какому-нибудь треду, с помощью метода enumerate(Thread[] tarray) получаешь массив активных тредов и перебором (tarray[i].getName() = "Имя") ищешь тред с нужным именем и - обращаешься. 3.По окончании работы работы тред чистит свое имя в листе и умирает - ИМХО, без фантомов Возможные осложнения: метод enumerate - static, возвращает "...every active thread in the current thread's thread group and its subgroups...". Что он вернет при вызове в виде Thread.enumerate(tarray) из, допустим, обработчика событий и какой тред будет при этом считаться current - совершенно не представляю, надо пробовать. И, btw, действительно ли необходимо устанавливать и рвать connection к БД в каждом треде? Через один постоянный connection в БД можно спокойно закачать то, что "нароет" целая куча ftp тредов |
| Автор: Domestic Cat 23.11.2004, 03:20 |
| Вообще-то идея "регистрации" тредов не совсем правильная в данном контексте. Треды ведь все одинаковы? КАждый тред самодостаточен ? Обеспечиваем взаимодействие через synchronized (notify, wait) и всего делов. Зачем же их искусственно "метить" ? |
| Автор: igon 23.11.2004, 05:29 | ||||||||
В контексте простого подсчета активных - да! Но ведь нужно
(btw, в классных переменных=переменных класса=static переменных нельзя хранить особенности состояния конкретного треда - они общие для всех instance класса) В случае
и "долгоживущих" тредов как мне узнать, сколько, скажем, информации "накачал" тред, созданный в 54 итерации, не используя сохраненные ссылки на него? Да и в GUI для мониторинга произвольных тредов лучше показывать их имена, а не адреса Еще раз подчеркну - идея далеко не бесспорна и действительно зависит от решаемых задач, но
|
| Автор: Domestic Cat 23.11.2004, 05:56 | ||||
глобальных переменных вообще не существует (в Java), р5аз уж на то пошло, статические переменные и есть в некором смысле глобальные.
раз все треды равны, то какая хрен разница какой тред что скачал, и вообще какое у треда имя. |
| Автор: Sleepy_PIP 23.11.2004, 12:03 | ||||||
при чем здесь стат. переменные.??? Я храню в классе тхреада не несколько переменных - они специфичны для каждого экземпляра класса. и они мне нужны!. По поводу именования - они (тхреады) и так автоматом именуются уникально, но хранить имена - мало. нужен доступ до переменных. |
| Автор: Domestic Cat 23.11.2004, 16:20 | ||
а можно узнат', что это зa переменныe? |
| Автор: Sleepy_PIP 23.11.2004, 17:32 | ||||
public class GetListThr extends Thread { public static final long SQLockConflict=335544345; public static final long SQLDeadLockConflict=335544336; public boolean StartMake=false; private PIPFTPSite ftps; private byte conncount=0; public int TryCount; public PIPLog llog=PIPFTPSGetList.Gconf.llog; public DBWork dbw=PIPFTPSGetList.Gconf.dbw; public DBConnItem dbc; public boolean notConnected; private PreparedStatement ub; private PreparedStatement ubl; public FTPClient lftp; private long CountDirList=0; .... я в чем-то ошибаюсь? где-то не правильно в корне? |
| Автор: Domestic Cat 23.11.2004, 18:15 | ||
Есть переменные, которые только треду нужны, а я имею в виду - значния каких полей тебе нужно "извне" смотреть :
То есть, зачем тебе нужен доступ к переменным? И к каким (только объяс)ни что они значат, по названиям можо толькo догадываться. И еwe раз прошу : пользуйся тегами [cоde=java] [/cоde]. |
| Автор: Sleepy_PIP 23.11.2004, 18:25 | ||||||
|
| Автор: Domestic Cat 23.11.2004, 18:53 |
| Все это можно делать, но в обратном направлении: например ты спрашиваешь тред извне сколько было попыток коннекта. Но можно передавать тредам ссылку на объект, который заведует статистикой, так чтобы треды сами сообщали ему нужые данные. Добавлено @ 18:54 ПС: спасибо за использование подсветки |
| Автор: Sleepy_PIP 23.11.2004, 19:07 | ||
| понял, понял. я это уже продумывал, но решил что так - банально писать меньше Добавлено @ 19:13
вообще что-то не так происходит у меня. - это я опять про листы и тхреады ... сделал контр. пример без листов (приводить не буду - много и муторно), но делающий то-ж самое - так колл-во тхреадов по тасклисту - почти соответствует расчетному и быстро отражает суть жизни тхреадов ... что-то я не понимаю. где-то переколдовал видать |
| Автор: Domestic Cat 23.11.2004, 19:14 |
| ну тогдa ищи проблему с листом, почему гц запаздывает со сборкой. Может, где-то еще ты хранишь ссылки на треды |
| Автор: Sleepy_PIP 23.11.2004, 19:19 | ||
Спасибо! буду копать .... |
| Автор: igon 23.11.2004, 22:54 | ||||||
Нижеприведенная демо-программа позволяет избавиться от ссылок в листах и тем не менее обеспечить доступ к переменным конкретных тредов. Правда, в win32 не знаю, как следить за фантомами
Программа - именно демо, так что оптимизацией здесь и не пахнет |
| Автор: Domestic Cat 24.11.2004, 06:39 |
| Делать так можно, только вот не все треды поймаешь - что если тред завершился до того, как его загнали в массив? И все равно ведь ты ссылки на треды хранишь в массиве - было, например 50 тредов, потом стало 10. А 40 ссылок в массиве висит. |
| Автор: igon 24.11.2004, 18:18 | ||||||||||
если (2) выполняется раньше (1) - пора менять профессию Ну, или, на худой конец, между (1) и (2) можно вставить Thread.sleep(1000)
list.add(a.getName()); добавляет в лист (массив) не ССЫЛКУ, а ИМЯ треда. ОК, чтобы более наглядно
Это абсолютно эквивалентно
Если под массивом подразумевался Thread[] - я ж говорю, что программа не оптимизирована. Вместо объявления стационарного заведомо большого массива правильнее определять его размерность из activeCount и далее использовать как локальную переменную в методе ActionPerformed |
| Автор: Sleepy_PIP 24.11.2004, 18:51 | ||||||||||||||||||
что-то я запутался. ведь:
_добавляет_ треад в массив activeThreadArray, и ты сам пишешь что для доступа к методам класса тхреада надо:
т.е. класс тхреада (ссылка на него) все-ж сидит в массиве, или нет? а имена ты как я понимаю хранишь в листе:
тогда я вообще не понимаю (не вижу у тебя) - как и когда ссылка на класс тхреада уходит из массива при завершении тхреада. Как имена удаляются - вижу, а как классы тхреадов - не понял ... Поясните кто может пожалуста, а? Спасибо! |
| Автор: Domestic Cat 24.11.2004, 19:02 | ||||||
Ты регистрируешь имена тредов, а потом каждую секундu их смотришь. Так воt, еслi тред завершается в 10:52:33 а ты делаешь enumerate в 10:52:56, кak ты вытянешь информацию с уже завершенного треда?
Об этом я и говорил.
Если массив сделать локальной переменной, то вse ок, тольko перформанс пострадаеt немногo. |
| Автор: igon 24.11.2004, 19:21 | ||
Примерно вот так
Ух ты, как вы быстро реагируете |
| Автор: igon 24.11.2004, 19:59 | ||||
| Вообще-то я "затачивался" на неиспользование ссылок в листе Чтобы перформансе не пострадал (за счет создания каждый раз нового массива) можно его (массив) чистить на выходе из ActionPerformed, присваивая null всем 200 элементам (на всякий случай). Но будет ли это быстрее?
то завершившийся в 10:52:33 тред в enumerate не попадет
И такая задача стоит??? Имя тред за собой чистит по завершении и исчезает БЕССЛЕДНО. И кто с него не успел вытянуть информацию - тот опоздал |