результаты потоковой работы неожиданны - Java - PullRequest
0 голосов
/ 29 октября 2018

Я новичок в многопоточности. Я работаю над программой, чтобы прочитать файл со многими записями (3 миллиона целых чисел) в некоторый связанный список, а затем некоторые потоки работают с каждым списком и вычисляют сумму списка, находя максимум и минимум. затем основной поток сравнивает результаты дочернего потока и отображает окончательные результаты.

Программа работы с этой командой:

compute -f file_path -t threads_Number

Теперь проблема в том, что когда я запускаю программу с одним потоком, это занимает меньше времени, чем у некоторых потоков.

мой процессор - Core i7.

это результат некоторого времени:

(Темы: длительность) -> (1: 16), (2,3: 32), (4,5,6,7: 47), (8,9: 31) ... (17, 18,19,20: 16)

В проекте есть 2 проекта:

Рабочий класс:

public class Worker implements Runnable {

    private List<Integer> records;
    private long[] res;
    private String name;

    Worker(String name, LinkedList<Integer> list, long[] res) {
        this.records = list;
        this.res = res;
        this.name = name;
    }

    @Override
    public void run() {

        long startTime = System.currentTimeMillis();
        long sum = 0;
        int max, min;

        if (records != null && records.size() > 0) {
            max = min = records.get(0);

            for (Integer num : records) {
                sum += num;
                if (num > max)
                    max = num;
                if (num < min)
                    min = num;
            }

            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;

            res[0] = sum;
            res[1] = max;
            res[2] = min;
            res[3] = duration;

            System.out.println(name + "\t->\ttime:\t" + duration + "\t, Records:\t" + records.size());
        }
    }
}

Основной класс:

public class Main {

    public static void main(String[] args) {
        //read command and get inputs:
        System.out.println("Welcome to my app : ");

        while (true) {

            Scanner scanner = new Scanner(System.in);
            String command = scanner.nextLine().trim();
            if (command.startsWith("compute")) {
                command = command.substring("compute".length() + 1);
                args = command.split(" ");
            } else {
                System.out.println("wrong command.. this app only support 'compute'");
                exit(1);
            }

            Map<String, String> map = new HashMap<>(); //-p processes , -f filepath
            for (int i = 0; i < args.length - 1; i += 2)
                map.put(args[i], args[i + 1]);

            File txtFile = new File(map.get("-f").trim());
            final int threadCount = Integer.parseInt(map.get("-t").trim());

            ArrayList<LinkedList<Integer>> lists = readFile(txtFile, threadCount);
            if (lists == null) {
                System.out.println("Error: can not found txt file..");
                exit(2);
            }

            long[][] results = new long[threadCount][4];
            Thread[] thread = new Thread[threadCount];

            for (int i = 0; i < threadCount; i++) {
                thread[i] = new Thread(new Worker("thread " + (i + 1) ,lists.get(i), results[i]));
                thread[i].start();
            }

            boolean isAlive = true;
            while (isAlive) {
                isAlive = false;
                for (int i = 0; i < threadCount; i++)
                    isAlive |= thread[i].isAlive();
            }

            long[] res = null;
            for (long[] result : results) {
                if (res != null) {
                    res[0] += result[0];
                    if (res[1] < result[1])
                        res[1] = result[1];
                    if (res[2] > result[2])
                        res[2] = result[2];
                    if (res[3] < result[3])
                        res[3] = result[3];
                } else {
                    res = result;
                }
            }

            if (res != null) {
                System.out.println("sum : " + res[0]);
                System.out.println("max : " + res[1]);
                System.out.println("min : " + res[2]);
                System.out.println("duration : " + res[3]);
            }

        }
    }

    private static ArrayList<LinkedList<Integer>> readFile(File txtFile, int procCount) {
        if(!txtFile.exists() || txtFile.isDirectory())
            return null;

        ArrayList<LinkedList<Integer>> arrayList = new ArrayList<>();

        for(int i = 0; i < procCount; i++)
            arrayList.add(new LinkedList<>());

        try {
            int index = 0;
            BufferedReader bufferedReader = new BufferedReader(new FileReader(txtFile));
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                arrayList.get(index).add(Integer.parseInt(line));
                index++;
                if(index == procCount)
                    index = 0;
            }

            return arrayList;
        }   catch (IOException e) {
            e.printStackTrace();
            return null;
        }

    }
}

Ответы [ 3 ]

0 голосов
/ 29 октября 2018

Я думаю, что для изучения многопоточности важно знать о законе Амдала. Закон Амдала может быть использован для вычисления того, насколько можно ускорить вычисление, запустив его часть параллельно. Я не хочу вдаваться в технические подробности, чтобы вы могли прочитать об этом в Википедии: https://en.wikipedia.org/wiki/Amdahl%27s_law

Закон Амдала в основном гласит, что ускорение параллельных вычислений не является линейным по отношению к количеству ваших потоков, которые зависят от количества ядер в вашем процессоре (если ваш код не поддерживает сетевые соединения, ввод-вывод и т. Д.). Следовательно, вы не можете ожидать, что ваша программа удвоит скорость, когда вы удвоите количество потоков.

Более того, класс Thread, как и любой другой класс в java, потребует много накладных расходов для создания и потребует много ресурсов. Поэтому создание большего количества потоков без эффективной балансировки входных данных только для арифметических операций (которые уже очень оптимизированы JVM) сделает скорость вашей программы непредсказуемой или даже более медленной.

Есть еще много проблем, которые необходимо рассмотреть. Небольшое предложение - использовать класс ExecutorService Java для управления вашими потоками. Удачного кодирования.

0 голосов
/ 30 октября 2018

Ваши "необычные" результаты, скорее всего, связаны с оптимизацией, выполненной компилятором JIT

Ваш код выполняет здесь «эталонный тест» с настраиваемым количеством потоков. Для таких тестов рекомендуется использовать JMH framework .

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

0 голосов
/ 29 октября 2018

Почему вы не используете Исполнители ?

С аргументом Integer.parseInt (map.get ("- t"). Trim ()) вы создаете пул потоков . И isAlive больше не требуется. И Результаты Концепция Будущее .

Пул потоков

Future

Исполнители - Управление потоками

Примеры

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...