Потоковое приложение записывает повторяющиеся строки в файл журнала

1

Я написал многопоточное приложение, которое анализирует строки в базе данных с регулярным выражением и обновляет их соответствующим образом. Я пишу каждую строку в файл журнала для ведения журнала. Я заметил, что одна и та же строка записывается в файл журнала несколько раз... иногда более 15 раз. Вот фрагменты кода.

Настройка ThreadPoolExecuter:

private static BlockingQueue<Runnable> worksQueue = new ArrayBlockingQueue<Runnable>(blockingQueueSize);
private static ThreadPoolExecutor exec = new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 10, TimeUnit.SECONDS, worksQueue);

В этой части я запускаю запрос, а затем просматриваю результаты:

rs = ps.executeQuery();

while (rs.next()) {
    exec.execute(new UpdateMember(rs, conn, fileWriter));

    if (worksQueue.size() == blockingQueueSize) {
        //reach the maximum, stop refill
        for (;;) {
            Thread.yield();
            //wait until the size of queue reached the minimum  
            if (worksQueue.size() == 0) {
                //start refill
                break;
            }
        }
    }
}

UpdateMember (показывается только запуск и метод writeToLog):

public class UpdateMember implements Runnable {

    ResultSet rs;
    Connection conn;
    FileWriter fw;

    public UpdateMember(ResultSet rs, Connection conn, FileWriter fw) {
        this.rs = rs;
        this.conn = conn;
        this.fw = fw;
    }

    @Override
    public void run() {
        try {
            String regex = "((?<city>[a-zA-Z\\s\\.]+)\\s)?(?<provState>AB|ALB|Alta|alberta|BC|B\\.C\\.|British Columbia|LB|Labrador|MB|Man|Manitoba|N[BLTSU]|Nfld|NF|Newfoundland|NWT|Northwest Territories|Nova Scotia|New Brunswick|Nunavut|ON|ONT|Ontario|PE|PEI|Prince Edward Island|QC|PC|QUE|QU|Quebec|SK|Sask|Saskatchewan|YT|Yukon|Yukon Territories)(\\s(?<country>CA|CAN|CANADA))?$";
            Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);

            BigDecimal memrecno = rs.getBigDecimal(2);
            String addressLineTwo = rs.getString(4);
            String addressLineThree = rs.getString(5);
            String addressLineFour = rs.getString(6);
            BigDecimal attrrecno = rs.getBigDecimal(9);

            String addressBeingParsed = "";
            String city = null;
            String province = null;
            String country = null;

            boolean usingAddressThree = false;
            boolean usingAddressFour = false;

            if (addressLineFour == null) {
                if (addressLineThree == null) {
                    city = "Unknown";
                }
                else
                {
                    addressBeingParsed = addressLineThree;
                    usingAddressThree = true;
                }
            }
            else
            {
                addressBeingParsed = addressLineFour;
                usingAddressFour = true;
            }

            if (usingAddressThree || usingAddressFour) {
                Matcher matcher = pattern.matcher(addressBeingParsed);

                // if matches are found
                if (matcher.matches()) {
                    city = matcher.group("city");
                    province = matcher.group("provState");
                    country = matcher.group("country");

                    if (city == null || city.isEmpty()) {
                        // cities are alpha characters and spaces only
                        String cityRegex = "(?<city>^[a-zA-Z\\s\\.]+$)";
                        Pattern cityPattern = Pattern.compile(cityRegex, Pattern.CASE_INSENSITIVE);

                        if (usingAddressFour && (addressLineThree != null) && !addressLineThree.isEmpty()) {
                            Matcher cityMatcher = cityPattern.matcher(addressLineThree);
                            if (cityMatcher.matches()) {
                                city = cityMatcher.group("city");
                            }
                            else
                            {
                                city = "Unknown";
                            }
                        }
                        else if (usingAddressThree && (addressLineTwo != null) && !addressLineTwo.isEmpty()) {
                            Matcher cityMatcher = cityPattern.matcher(addressLineTwo);
                            if (cityMatcher.matches()) {
                                city = cityMatcher.group("city");
                            }
                            else
                            {
                                city = "Unknown";
                            }
                        }
                        else
                        {
                            city = "Unknown";
                        }
                    }

                    if (province != null && !province.isEmpty()) {
                        province = createProvinceCode(province);
                    }
                }
                else
                {
                    city = "Unknown";
                }
            }

            // update attributes in database
            boolean success = updateRow(memrecno, attrrecno, city, province);

            String logLine = memrecno.toString() + "|" + attrrecno.toString() + "|" + addressLineTwo + "|" + addressLineThree + "|" + addressLineFour + "|" + city + "|" + province + "|" + country + "|" + success + "|" + String.valueOf(Thread.currentThread().getId());

            writeToLog(logLine);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    private synchronized void writeToLog(String line) {
        try {
            fw.write(line + "\r\n");
            fw.flush();
        }
        catch (IOException ex)
        {
            System.out.println("Error writing to log file. " + ex.getMessage());
        }
    }
}

Я не знаю, если потоки также вызывают метод updateRow несколько раз, но я предполагаю, что они есть и что очень плохо.

Любые идеи относительно того, почему это будет сделано?

Теги:
multithreading

1 ответ

3

Я не думаю, что ResultSet является потокобезопасным. Из вашего кода вы должны сначала получить значение, а затем передать значение вместо rs в поток.

  • 0
    Спасибо ... похоже ты прав. ResultSet не является потокобезопасным. Ни оператор, ни PreparedStatement. Могу ли я создать новый объект ResultSet и назначить его rs? Или я получу проблемы, потому что это передается по ссылке?
  • 0
    Я не уверен, почему вы должны передать ResultSet исполнителю, потому что ResultSet имеет только курсор на данные в базе данных. Вы можете использовать Object [], чтобы сохранить значение, а затем передать его в поток. Если вы хотите использовать набор результатов для обновления данных в базе данных, я думаю, что хорошим решением является распространение условия вашего запроса на разные потоки, которые могут использовать набор результатов сами.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню