Почему заполнение списка массивов фиктивными записями занимает много времени в многопоточной среде? - PullRequest
0 голосов
/ 22 октября 2019

Я пытался добавить 10 миллионов записей в список массивов, используя пул потоков размером 4 (на восьмиъядерном процессоре). Но это занимает вдвое больше времени по сравнению с однопоточным кодом.

Ниже приведены фрагменты кода. Я могу делать что-то не так. Может кто-нибудь объяснить, в чем проблема в коде?

package com.shree.test;

public class Student {
    private int id;
    private String name;
    private int age;
    private int std;

    public Student(int id, String name, int age, int std) {
        super();
        this.id = id;
        this.name = name;
        this.age = age;
        this.std = std;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public int getStd() {
        return std;
    }
    public void setStd(int std) {
        this.std = std;
    }
    @Override
    public String toString() {
        return "Student [id=" + id + ", name=" + name + ", age=" + age + ", std=" + std + "]";
    }
}

Многопоточный код (с Threadpool):

    package com.shree.test;

    import java.time.LocalDateTime;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;

    import org.apache.commons.lang3.RandomStringUtils;
    import org.apache.commons.lang3.RandomUtils;

    class Task implements Callable<Student>{

        private static final String CHAR_SET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
        private int id;
        private List<Student> studentList;

        public Task(int id,List<Student> studentList) {
            this.id = id;
            this.studentList = studentList;
        }

        @Override
        public Student call() throws Exception {
            Student student =  new Student(id, RandomStringUtils.random(RandomUtils.nextInt(5, 10), CHAR_SET), RandomUtils.nextInt(10, 15), RandomUtils.nextInt(4, 9));
            studentList.add(student);
            return student;
        }
    }

    public class MultiThreadStudentListGenerator {

        private List<Student> students = Collections.synchronizedList(new ArrayList<>());

        private ExecutorService threadPool = Executors.newFixedThreadPool(4);

        public void generateStudentList() {
            for(int i=0;i<10000000;i++) {
                threadPool.submit(new Task(i, students));
            }
            threadPool.shutdown();  
        }

        public void process() {
            generateStudentList();
        }

        public int getSize() {
            return students.size();
        }

        public void addShutDownhook(LocalDateTime dateTime1 ) {
            Runtime.getRuntime().addShutdownHook(new Thread() {
                public void run() {
                    LocalDateTime dateTime2 = LocalDateTime.now();

                    long diffInMilli = java.time.Duration.between(dateTime1, dateTime2)
                            .toMillis();

                    System.out.println("Time taken in Miliseconds: " + diffInMilli);
                    System.out.println("List Size: " + getSize());
                }
            });
        }

        public static void main(String[] args) {

            MultiThreadStudentListGenerator multiThreadStudentListGenerator = new MultiThreadStudentListGenerator();

            LocalDateTime dateTime1 = LocalDateTime.now();
            multiThreadStudentListGenerator.addShutDownhook(dateTime1);

            multiThreadStudentListGenerator.process();
        }
    }

Однопоточный код:

package com.shree.test;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.RandomUtils;

public class SingleThreadStudentListGenerator {

    private static final String CHAR_SET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    private List<Student> students = new ArrayList<>();

    public void generateStudentList() {
        for (int i = 0; i < 10000000; i++) {
            Student student = new Student(i, RandomStringUtils.random(RandomUtils.nextInt(5, 10), CHAR_SET),
                    RandomUtils.nextInt(10, 15), RandomUtils.nextInt(4, 9));
            students.add(student);
        }
    }

    public void process() {
        generateStudentList();
    }

    public int getSize() {
        return students.size();
    }

    public void addShutDownhook(LocalDateTime dateTime1) {
        Runtime.getRuntime().addShutdownHook(new Thread() {
            public void run() {
                LocalDateTime dateTime2 = LocalDateTime.now();

                long diffInMilli = java.time.Duration.between(dateTime1, dateTime2).toMillis();

                System.out.println("Time taken in Miliseconds: " + diffInMilli);
                System.out.println("Size: " + getSize());
            }
        });
    }

    public static void main(String[] args) {

        SingleThreadStudentListGenerator mainClass = new SingleThreadStudentListGenerator();

        LocalDateTime dateTime1 = LocalDateTime.now();
        mainClass.addShutDownhook(dateTime1);

        mainClass.process();

    }
}

Ответы [ 2 ]

1 голос
/ 22 октября 2019

Две основные проблемы:

  • как вы измеряете. Ваша идея использовать shutdown hook , это действительно странно. Вам лучше использовать такую ​​среду, как JMH, чтобы выполнить такое тестирование, см. здесь , чтобы узнать, как получить значащие числа
  • тогда, здесь: Collections.synchronizedList(new ArrayList<>()). Это создает список, который синхронизирует при запросах на добавление / удаление. Это означает: блокировка. Угадайте, что: блокировка стоит дорого.

Другими словами: A) способ получения ваших номеров сомнителен, а B) блокировка дороже, чем не блокировка. В вашем случае ваши 4 потока будут постоянно сталкиваться друг с другом, и им придется ждать, пока другой поток завершит изменение списка.

Только представьте: когда у вас есть одна лопата, и вам нужно вырыть яму ... Вы действительно выиграете от найма 4 парней, чтобы сделать работу? Или лучше: 3 парня наблюдают за четвертым, используя лопату?!

Как в: обратите внимание, что использование нескольких потоков только экономит ваше общее время выполнения в определенных ситуациях, например, когда вашрабочая нагрузка должна ждать ввода-вывода очень часто. Интенсивная загрузка процессора (и это то, что делает ваш код) не приносит пользы от «большего количества потоков». Иначе. Больше потоков, что может означать переключение контекста, блокировку, менее эффективное использование кэшей ЦП и так далее.

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

0 голосов
/ 22 октября 2019

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

Вместо этого рассмотрите возможность разбить создание списка на 4 параллельных задания и объединить их, чтобы создать список из 10 миллионов студентов.

...