Существует два основных способа многопоточности в Java. Каждая логическая задача, которую вы создаете с помощью этих методов, должна запускаться на новом ядре, когда это необходимо и доступно.
Метод первый: определить объект Runnable или Thread (который может принимать Runnable в конструкторе) и запустить его с помощью метода Thread.start (). Он будет работать на любом ядре, которое дает ОС - как правило, менее загруженном.
Учебное пособие: Определение и запуск потоков
Метод два: определяют объекты, реализующие интерфейс Runnable (если они не возвращают значения) или Callable (если они делают), которые содержат код обработки. Передайте их как задачи в ExecutorService из пакета java.util.concurrent. Класс java.util.concurrent.Executors имеет множество методов для создания стандартных, полезных видов ExecutorServices. Ссылка на учебник для исполнителей.
Исходя из личного опыта, исправленные и кэшированные пулы потоков в Executors очень хороши, хотя вы захотите изменить количество потоков. Runtime.getRuntime (). AvailableProcessors () может использоваться во время выполнения для подсчета доступных ядер. Когда приложение будет готово, вам необходимо будет закрыть пулы потоков, иначе приложение не закроется, поскольку потоки ThreadPool продолжают работать.
Получение хорошей многоядерной производительности иногда сложно и полно ошибок:
- Дисковый ввод / вывод замедляет LOT при запуске в
параллельно. Только один поток должен выполнять чтение / запись диска одновременно.
- Синхронизация объектов обеспечивает безопасность многопоточных операций, но замедляет работу.
- Если задачи слишком
тривиальный (маленькие рабочие биты, выполнить
быстро) накладные расходы на управление ими
в ExecutorService стоит больше, чем
вы получаете от нескольких ядер.
- Создание новых объектов Thread выполняется медленно. ExecutorServices попытается повторно использовать существующие потоки, если это возможно.
- Все виды сумасшедших вещей могут случиться, когда над чем-то работают несколько потоков Сделайте вашу систему простой и постарайтесь сделать задачи логически различными и не взаимодействующими.
Еще одна проблема: контролировать работу сложно! Хорошей практикой является наличие одного потока менеджера, который создает и отправляет задачи, а затем пара рабочих потоков с рабочими очередями (используя ExecutorService).
Я просто касаюсь ключевых моментов: многопоточное программирование многими экспертами считается одной из самых сложных тем программирования. Это не интуитивно, сложно, и абстракции часто слабы.
Редактировать - пример с использованием ExecutorService:
public class TaskThreader {
class DoStuff implements Callable {
Object in;
public Object call(){
in = doStep1(in);
in = doStep2(in);
in = doStep3(in);
return in;
}
public DoStuff(Object input){
in = input;
}
}
public abstract Object doStep1(Object input);
public abstract Object doStep2(Object input);
public abstract Object doStep3(Object input);
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
ArrayList<Callable> tasks = new ArrayList<Callable>();
for(Object input : inputs){
tasks.add(new DoStuff(input));
}
List<Future> results = exec.invokeAll(tasks);
exec.shutdown();
for(Future f : results) {
write(f.get());
}
}
}