Java: как масштабировать потоки в соответствии с ядрами процессора?

Я не хороший Java-программист, это просто мое хобби, но я очень хочу узнать больше, чем средний материал.

Я хочу решить математическую проблему с несколькими потоками в java. моя математическая проблема может быть разделена на рабочие единицы, которые я хочу решить в нескольких потоках.

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

Так вы могли бы мне помочь со ссылкой на хороший тутурий или могли бы дать мне простой и хороший пример? Это было бы очень приятно:)

Ответ 1

Вы можете определить количество процессов, доступных для виртуальной машины Java, используя статический метод Runtime, availableProcessors. После того, как вы определили количество доступных процессоров, создайте это количество потоков и разделите свою работу соответственно.

Обновить. Чтобы уточнить, Thread - это просто объект в Java, поэтому вы можете создать его так же, как и любой другой объект. Итак, позвольте сказать, что вы вызываете вышеупомянутый метод и обнаруживаете, что он возвращает 2 процессора. Потрясающие. Теперь вы можете создать цикл, который генерирует новый поток, и разделяет работу на этот поток и запускает поток. Вот несколько psuedocode, чтобы продемонстрировать, что я имею в виду:

int processors = Runtime.getRuntime().availableProcessors();
for(int i=0; i < processors; i++) {
  Thread yourThread = new AThreadYouCreated();
  // You may need to pass in parameters depending on what work you are doing and how you setup your thread.
  yourThread.start();
}

Для получения дополнительной информации о создании собственного потока обратитесь к этому руководству. Кроме того, вы можете посмотреть Пул потоков для создания потоков.

Ответ 2

Вероятно, вы захотите посмотреть на инфраструктуру java.util.concurrent для этого. Что-то вроде:

ExecutorService e = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
// Do work using something like either
e.execute(new Runnable() {
        public void run() {
            // do one task
        }
    });

или

    Future<String> future = pool.submit(new Callable<String>() {
        public String call() throws Exception {
            return null;
        }
    });
    future.get();  // Will block till result available

Это намного лучше, чем справляться с вашими пулами потоков и т.д.

Ответ 3

Дуг Ли (автор параллельного пакета) имеет этот документ, который может иметь значение: http://gee.cs.oswego.edu/dl/papers/fj.pdf

В Java SE 7 добавлена ​​структура Fork Join. Ниже приведены несколько ссылок:

http://www.ibm.com/developerworks/java/library/j-jtp11137/index.html Статья Брайана Гетца

http://www.oracle.com/technetwork/articles/java/fork-join-422606.html

Ответ 4

В классе Runtime существует метод, называемый доступнымиProcessors(). Вы можете использовать это, чтобы выяснить, сколько у вас процессоров. Поскольку ваша программа связана с процессором, вы, вероятно, захотите иметь (не более) один поток на каждый доступный CPU.

Ответ 5

Стандартным способом является метод Runtime.getRuntime(). availableProcessors(). На большинстве стандартных процессоров вы вернете оптимальный подсчет потока (который не является фактическим количеством ядер процессора). Поэтому это то, что вы ищете.

Пример:

ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

Не забудьте закрыть службу-исполнитель, как это (или ваша программа не выйдет):

service.shutdown();

Вот только краткое описание того, как настроить будущий MT-код (offtopic, для иллюстрации):

CompletionService<YourCallableImplementor> completionService = 
    new ExecutorCompletionService<YourCallableImplementor>(service);
    ArrayList<Future<YourCallableImplementor>> futures = new ArrayList<Future<YourCallableImplementor>>();
    for (String computeMe : elementsToCompute) {
        futures.add(completionService.submit(new YourCallableImplementor(computeMe)));
    }

Затем вам нужно отслеживать, сколько ожидаемых результатов и получить их следующим образом:

try {
  int received = 0;
  while (received < elementsToCompute.size()) {
     Future<YourCallableImplementor> resultFuture = completionService.take(); 
     YourCallableImplementor result = resultFuture.get();
     received++; 
  }
} finally {
  service.shutdown();
}

Ответ 6

Вариант 1:

newWorkStealingPool от Executors

public static ExecutorService newWorkStealingPool()

Создает пул потоков обработки, используя все доступные процессоры в качестве целевого уровня parallelism.

С помощью этого API вам не нужно передавать количество ядер в ExecutorService.

Внедрение этого API из grepcode

/**
     * Creates a work-stealing thread pool using all
     * {@link Runtime#availableProcessors available processors}
     * as its target parallelism level.
     * @return the newly created thread pool
     * @see #newWorkStealingPool(int)
     * @since 1.8
     */
    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

Вариант 2:

newFixedThreadPool API от Executors или other newXXX constructors, который возвращает ExecutorService

public static ExecutorService newFixedThreadPool(int nThreads)

заменить nThreads на Runtime.getRuntime().availableProcessors()

Вариант 3:

ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                      int maximumPoolSize,
                      long keepAliveTime,
                      TimeUnit unit,
                      BlockingQueue<Runnable> workQueue)

передать Runtime.getRuntime().availableProcessors() как параметр в maximumPoolSize.