2009-12-30 9 views
87

Я не хороший программист на Java, это просто мое хобби, но я очень хочу узнать больше, чем средний материал.Java: Как масштабировать потоки в соответствии с ядрами процессора?

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

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

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

ответ

99

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

Обновление: Чтобы пояснить, нить представляет собой просто объект в 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(); 
} 

Для получения дополнительной информации о создании собственной нити, head to this tutorial. Кроме того, вы можете посмотреть на Thread Pooling для создания потоков.

+12

Это в основном правильно, но будьте осторожны с производительностью на процессорах, продаваемых с помощью «гиперпотоков» Intel. На четырехъядерном процессоре это вернет 8 вместо 4, но ваша производительность может фактически начать падение после 4 потоков - так что мои собственные тесты говорят мне :) – xcut

+0

Привет, хорошо, не знал, что это возможно. , но когда я разбил одну задачу на несколько рабочих единиц, и мне нужно все решение части для окончательной работы, как это делается? Когда у меня есть несколько «yourThreads», как использовать для этого join(), потому что я не вижу, как эти несколько потоков различаются? :) BTW: ваша ссылка на пул потоков приводит меня к http://www.ibm.com/developerworks/library/j-jtp0730.html :) –

+4

Посмотрите на пример здесь: http: // java. sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ExecutorService.html Он расскажет вам об упрощенном способе создания и управления пулом потоков. Сначала это может показаться более сложным, но как и в большинстве случаев, это сложнее, потому что, если бы это было проще, вы бы скорее ударили ограничения раньше. –

4

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

+0

Привет, Джейсон и Эрик (я использую один комментарий для обоих ваших ответов, потому что это в основном то же самое). хорошо, это приятно проверить, но это будет первая часть. Когда у меня есть счет ядра, мне нужно, чтобы потоки были такими переменными, как это количество ядер. Я пробовал этот пример до того, как http://openbook.galileodesign.de/javainsel5/javainsel09_003.htm#Rxx747java09003040002E31F0491F9 (немецкий!) И использует фиксированный поток. Но я хочу иметь такое же программирование, используя 2 ядра в двухъядерной среде и 4 ядра в четырехъядерной среде. Я не хочу менять его вручную. Возможно ли это? THX! :) –

+0

@ Andreas - Смотрите обновления, которые я сделал на свой пост.Я думаю, это поможет прояснить эту проблему. – JasCav

59

Возможно, вы захотите посмотреть на инфраструктуру 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 

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

+0

Привет, DaveC, Хммм, не знаю этого раньше, поэтому я посмотрю на это. И его можно масштабировать в соответствии с доступными ядрами процессора? Потому что я не вижу этого в ваших коротких примерах. С наилучшими пожеланиями, Andreas –

+3

java.util.concurrent очень масштабируема –

+4

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

7

Doug Lea (автор параллельного пакета) имеет этот документ, в котором могут быть релевантными: http://gee.cs.oswego.edu/dl/papers/fj.pdf

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

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

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

4

Стандартный способ является 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(); 
} 
+2

звонок выключения должен быть поставлен, наконец, –

+0

@ КристофеРусси, ты очень прав, я изменил этот сниппет, спасибо! – fl0w

6

Вариант 1:

newWorkStealingPool из Executors

public static ExecutorService newWorkStealingPool() 

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

С помощью этого 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.

Смежные вопросы