Пишу многопоточный парсер. Класс парсера выглядит следующим образом:
| Код | public class Parser extends HTMLEditorKit.ParserCallback implements Runnable { private static List<Station> itemList = Collections.synchronizedList(new ArrayList<Item>()); private boolean h2Tag = false; private int count; private static int threadCount = 0;
public static List<Item> parse() { for (int i = 1; i <= 1000; i++) { //1000 однотипных страниц, которые нужно распарсить while (threadCount == 20) { //ограничиваем количество одновременно работающих потоков try { Thread.sleep(50); } catch (InterruptedException ex) { ex.printStackTrace(); } }
Thread thread = new Thread(new Parser()); thread.setName(Integer.toString(i)); threadCount++; //увеличиваем количество работающих потоков thread.start(); }
return itemList; }
public void run() { //Здесь идет кусок кода, отвечающий за формирование ссылки на основании //переданного как имя потока парамера i, подключение, запуск парсинга и т.д. //В общем ничего особенного. Приводить его не буду.
threadCount--; //умешаем число работающих потоков при завершении текущего }
private static void addItem(Item item) { itenList.add(item); }
//в этом методе извлекаем нужную информацию после того, как обнаружен тег H2 @Override public void handleText(char[] data, int pos) { if (h2Tag) { String itemName = new String(data).trim();
//Item - элемент, о котором мы получаем информацию из веб-страницы Item item = new Item(); item.setName(itemName); item.setId(count); addItem(item); //выводим информацию об элементе в консоль System.out.println(count + " = " + itemName); } }
@Override public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) { if (HTML.Tag.H2 == t) { h2Tag = true; } }
@Override public void handleEndTag(HTML.Tag t, int pos) { if (HTML.Tag.H2 == t) { h2Tag = false; } } } |
Из другого класса парсер запускается так:
| Код | List<Item> list = Parser.parse(); |
Все бы хорошо, но есть проблема. По завершении парсинга в итоговом списке List<Item> itemList содержится 980 элеметов, вместо 1000. Хотя в консоли их оказывается 1000. Т.е. некоторые потоки почему-то не вызывают метод addItem в методе handleText. Я уже пытался менять тип itemList на ArrayList, CopyOnWriteArrayList, Vector. Делал метод addItem synchronized, менял его вызов на synchronized блок. Все это лишь меняет количество элементов на +-5, но итоговой тысячи получить не удается. Также я пытался парсить меньшее количество страниц (десять). В списке не оказывается ничего, в консоли все 10. Если многопоточность убрать, то все работает отлично, но, конечно, в разы медленнее, что не хорошо. Если уменьшать количество одновременно работающих потоков, то количество элементов в списке приближается к желанной 1000, если увеличивать - немного удаляется от 1000. Т.е., как мне кажется, здесь явно борьба за возможность записи в список. Но тогда почему не действует синхронизация? В чем проблема?
P.S. Также буду рад услышать просто замечания по коду. |