Top Banner
multithreading #multithread ing
23

multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

Aug 05, 2020

Download

Documents

dariahiddleston
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

multithreading

#multithread

ing

Page 2: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

1

1: 2

2

Examples 2

2

2

3

3

4

Hello Multithreading - 7

? 8

2: 9

9

9

10

ThreadPools 10

Examples 11

ThreadPool 11

12

Runnables Callables 13

ThreadFactory 15

3: 17

17

17

17

Mutex 17

Examples 17

Mutex Java C ++ 17

21

Page 3: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

ОколоYou can share this PDF with anyone you feel could benefit from it, downloaded the latest version from: multithreading

It is an unofficial and free multithreading ebook created for educational purposes. All the content is extracted from Stack Overflow Documentation, which is written by many hardworking individuals at Stack Overflow. It is neither affiliated with Stack Overflow nor official multithreading.

The content is released under Creative Commons BY-SA, and the list of contributors to each chapter are provided in the credits section at the end of this book. Images may be copyright of their respective owners unless otherwise specified. All trademarks and registered trademarks are the property of their respective company owners.

Use the content presented in this book at your own risk; it is not guaranteed to be correct nor accurate, please send your feedback and corrections to [email protected]

https://riptutorial.com/ru/home 1

Page 4: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

глава 1: Начало работы с многопоточным

замечания

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

При разработке многопоточной программы потоки должны быть сделаны независимо друг от друга, насколько это возможно, для достижения максимального ускорения. На практике потоки редко полностью независимы, что делает синхронизацию необходимой. Максимальное теоретическое ускорение можно рассчитать по закону Амдаля .

преимущества

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

Легко определять приоритеты определенных операций над другими•

Недостатки

Без тщательного проектирования могут быть введены труднодоступные ошибки•Создание потоков связано с некоторыми накладными расходами•

Examples

Цель

Темы - это части низкого уровня вычислительной системы, обработка команд. Он поддерживается / предоставляется аппаратным обеспечением CPU / MCU. Существуют также программные методы. Цель многопоточности делает расчеты параллельно друг другу, если это возможно. Таким образом, желаемый результат может быть получен в меньшем временном разрезе.

Тупики

Тупик возникает, когда каждый член какой-либо группы из двух или более потоков должен ждать, пока один из других членов что-то предпримет (например, чтобы освободить блокировку), прежде чем он сможет продолжить. Без вмешательства потоки будут ждать вечно.

https://riptutorial.com/ru/home 2

Page 5: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

Псевдокод пример тупиковой конструкции:

thread_1 { acquire(A) ... acquire(B) ... release(A, B) } thread_2 { acquire(B) ... acquire(A) ... release(A, B) }

Тупик может произойти, когда thread_1 приобрел A , но еще не B , а thread_2 приобрел B , но не A Как показано на следующей диаграмме, оба потока будут ждать всегда.

Как избежать взаимоблокировокКак общее правило, минимизируйте использование блокировок и минимизируйте код между блокировкой и разблокировкой.

Приобретение замков в том же порядке

Реорганизация thread_2 решает проблему:

thread_2 { acquire(A) ... acquire(B) ... release(A, B) }

Оба потока получают ресурсы в том же порядке, что позволяет избежать взаимоблокировок.

Это решение известно как «Решение иерархии ресурсов». Он был предложен Дейкстре как решение проблемы «Обеденные философы».

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

Рассмотрим следующий код:

https://riptutorial.com/ru/home 3

Page 6: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

void doCriticalTask(Object A, Object B){ acquire(A){ acquire(B){ } } }

Здесь, даже если порядок сбора блокировки выглядит безопасным, он может вызвать тупик, когда thread_1 обращается к этому методу, например, Object_1 как параметр A и Object_2 как параметр B, а thread_2 выполняет в противоположном порядке, то есть Object_2, как параметр A и Object_1 в качестве параметра B.

В такой ситуации лучше иметь какое-то уникальное условие, полученное с использованием как Object_1, так и Object_2 с каким-то вычислением, например, с использованием hashcode

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

например, Say Object имеет уникальный ключ, например accountNumber в случае объекта Account.

void doCriticalTask(Object A, Object B){ int uniqueA = A.getAccntNumber(); int uniqueB = B.getAccntNumber(); if(uniqueA > uniqueB){ acquire(B){ acquire(A){ } } }else { acquire(A){ acquire(B){ } } } }

Условия гонки

Годом данных или состоянием гонки является проблема, которая может возникнуть, если многопоточная программа не синхронизирована должным образом. Если два или более потока обращаются к одной и той же памяти без синхронизации, и, по крайней мере, один из способов доступа является «записью», происходит гонка данных. Это приводит к зависящему от платформы, возможно, непоследовательному поведению программы. Например, результат вычисления может зависеть от планирования потоков.

Читатели-писатели Проблема :

writer_thread {

https://riptutorial.com/ru/home 4

Page 7: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

write_to(buffer) } reader_thread { read_from(buffer) }

Простое решение:

writer_thread { lock(buffer) write_to(buffer) unlock(buffer) } reader_thread { lock(buffer) read_from(buffer) unlock(buffer) }

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

Решение, которое позволяет избежать этой проблемы, может быть:

writer_thread { lock(reader_count) if(reader_count == 0) { write_to(buffer) } unlock(reader_count) } reader_thread { lock(reader_count) reader_count = reader_count + 1 unlock(reader_count) read_from(buffer) lock(reader_count) reader_count = reader_count - 1 unlock(reader_count) }

Обратите внимание, что reader_count заблокирован на протяжении всей операции записи, так что ни один читатель не может начать чтение, пока запись еще не закончена.

Теперь многие читатели могут читать одновременно, но может возникнуть новая проблема: reader_count может никогда не достигнуть 0 , так что нить писателя никогда не сможет записать в буфер. Это называется голодом , есть разные решения, чтобы избежать этого.

https://riptutorial.com/ru/home 5

Page 8: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

Даже программы, которые могут показаться правильными, могут быть проблематичными:

boolean_variable = false writer_thread { boolean_variable = true } reader_thread { while_not(boolean_variable) { do_something() } }

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

C ++ и Java определяют в так называемой модели памяти, что правильно синхронизировано означает: модель памяти C ++, модель памяти Java .

В Java решение было бы объявить поле изменчивым:

volatile boolean boolean_field;

В C ++ решением было бы объявить поле как атомное:

std::atomic<bool> data_ready(false)

Гонка данных - это своего рода состояние гонки. Но не все условия гонки - это гонки данных. Следующие, вызванные более чем одним потоком, приводят к состоянию гонки, но не к гонке данных:

class Counter { private volatile int count = 0; public void addOne() { i++; } }

Он правильно синхронизирован в соответствии с спецификацией Java Memory Model,

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

Не все расы данных являются ошибками. Примером так называемого доброкачественного состояния гонки является sun.reflect.NativeMethodAccessorImpl:

https://riptutorial.com/ru/home 6

Page 9: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

class NativeMethodAccessorImpl extends MethodAccessorImpl { private Method method; private DelegatingMethodAccessorImpl parent; private int numInvocations; NativeMethodAccessorImpl(Method method) { this.method = method; } public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException { if (++numInvocations > ReflectionFactory.inflationThreshold()) { MethodAccessorImpl acc = (MethodAccessorImpl) new MethodAccessorGenerator(). generateMethod(method.getDeclaringClass(), method.getName(), method.getParameterTypes(), method.getReturnType(), method.getExceptionTypes(), method.getModifiers()); parent.setDelegate(acc); } return invoke0(method, obj, args); } ... }

Здесь производительность кода важнее, чем правильность подсчета numInvocation.

Hello Multithreading - создание новых потоков

Этот простой пример показывает, как запустить несколько потоков в Java. Обратите внимание, что потоки не гарантируются для выполнения в порядке, и порядок выполнения может варьироваться для каждого прогона.

public class HelloMultithreading { public static void main(String[] args) { for (int i = 0; i < 10; i++) { Thread t = new Thread(new MyRunnable(i)); t.start(); } } public static class MyRunnable implements Runnable { private int mThreadId; public MyRunnable(int pThreadId) { super(); mThreadId = pThreadId; } @Override public void run() {

https://riptutorial.com/ru/home 7

Page 10: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

System.out.println("Hello multithreading: thread " + mThreadId); } } }

Может ли тот же поток работать дважды?

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

Ответ для этого состоит в том, что один поток может запускаться только один раз.

если вы попытаетесь запустить тот же самый поток дважды, он будет выполняться в первый раз, но будет давать ошибку во второй раз, и ошибка будет IllegalThreadStateException.

пример :

public class TestThreadTwice1 extends Thread{ public void run(){ System.out.println("running..."); } public static void main(String args[]){ TestThreadTwice1 t1=new TestThreadTwice1(); t1.start(); t1.start(); } }

выход :

running Exception in thread "main" java.lang.IllegalThreadStateException

Прочитайте Начало работы с многопоточным онлайн: https://riptutorial.com/ru/multithreading/topic/1229/начало-работы-с-многопоточным

https://riptutorial.com/ru/home 8

Page 11: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

глава 2: Исполнители

Синтаксис

ThreadPoolExecutor•

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

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

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

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

Executors.callable(PrivilegedAction<?> action)•

Executors.callable(PrivilegedExceptionAction<?> action)•

Executors.callable(Runnable task)•

Executors.callable(Runnable task, T result)•

Executors.defaultThreadFactory()•

Executors.newCachedThreadPool()•

Executors.newCachedThreadPool(ThreadFactory threadFactory)•

Executors.newFixedThreadPool(int nThreads)•

Executors.newFixedThreadPool(int nThreads, ThreadFactory threadFactory)•

Executors.newScheduledThreadPool(int corePoolSize)•

Executors.newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)•

Executors.newSingleThreadExecutor()•

Executors.newSingleThreadExecutor(ThreadFactory threadFactory)•

параметры

параметр подробность

corePoolSize Минимальное количество потоков для хранения в пуле.

maximumPoolSize Максимальное количество потоков для пула.

https://riptutorial.com/ru/home 9

Page 12: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

параметр подробность

KeepAliveTimeКогда количество потоков больше, чем ядро, потоки noncore (

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

единица измерения

Единица времени для keepAliveTime .

Тайм-аут максимальное время ожидания

workQueue Тип очереди, которую наш Исполнитель собирается использовать

threadFactoryЗавод, который будет использоваться при создании новых потоков

nThreads Количество потоков в пуле

исполнитель Основная реализация

задача задача запуска

результат Результат возврата

действие Привилегированное действие для запуска

подлежащий выкупу

Основная задача

замечания

Различные типы потоков и очереди, описанные ниже, были взяты из информации и знаний из блога [oracle documentation] [1] и [Jakob Jenkov] [2], где вы можете много узнать о параллелизме в Java.

Различные типы ThreadPools

SingleThreadExecutor: Executor, который использует один рабочий поток, работающий с неограниченной очередью, и использует предоставленный ThreadFactory для создания нового потока, когда это необходимо. В отличие от эквивалентного newFixedThreadPool (1,

threadFactory), возвращаемый исполнитель гарантированно не может быть перенастроен для использования дополнительных потоков.

FixedThreadPool: пул потоков, который повторно использует фиксированное количество потоков, работающих с общей неограниченной очередью, используя предоставленный

https://riptutorial.com/ru/home 10

Page 13: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

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

CachedThreadPool: пул потоков, который при необходимости создает новые потоки, но будет использовать ранее созданные потоки, когда они будут доступны, и использует предоставленный ThreadFactory для создания новых потоков, когда это необходимо.

SingleThreadScheduledExecutor: однопоточный исполнитель, который может планировать выполнение команд по заданной задержке или выполнять их периодически. (Обратите внимание, что если этот единственный поток завершается из-за сбоя во время выполнения до выключения, новый, если потребуется, будет занят, чтобы выполнять последующие задачи.) Гарантируется выполнение задач последовательно и не более одной задачи будет активна в любой момент времени. В отличие от иначе эквивалентного newScheduledThreadPool (1, threadFactory), возвращаемый исполнитель гарантированно не может быть перенастроен для использования дополнительных потоков.

ScheduledThreadPool: пул потоков, который может планировать выполнение команд после заданной задержки или выполнять их периодически. Различные типы рабочих очередей

Examples

Определение новой ThreadPool

ThreadPool - это ExecutorService который выполняет каждую отправленную задачу, используя один из, возможно, нескольких объединенных потоков, обычно настраиваемых с использованием фабричных методов Executors.

Вот базовый код для инициализации нового ThreadPool в качестве одноэлементного приложения для вашего приложения:

public final class ThreadPool { private static final String TAG = "ThreadPool"; private static final int CORE_POOL_SIZE = 4; private static final int MAX_POOL_SIZE = 8; private static final int KEEP_ALIVE_TIME = 10; // 10 seconds private final Executor mExecutor; private static ThreadPool sThreadPoolInstance; private ThreadPool() {

https://riptutorial.com/ru/home 11

Page 14: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

mExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); } public void execute(Runnable runnable) { mExecutor.execute(runnable); } public synchronized static ThreadPool getThreadPoolInstance() { if (sThreadPoolInstance == null) { Log.i(TAG, "[getThreadManagerInstance] New Instance"); sThreadPoolInstance = new ThreadPool(); } return sThreadPoolInstance; } }

У вас есть два способа вызова метода runnable, используйте execute() или submit() . разница между ними заключается в том, что submit() возвращает объект Future который позволяет вам программно отменить Callable поток, когда объект T возвращается из обратного вызова Callable . Вы можете больше узнать о Future здесь

Будущие и вызывающие

Одной из функций, которые мы можем использовать с Threadpool, является метод submit() который позволяет нам знать, когда поток завершает работу. Мы можем сделать это благодаря объекту Future , который возвращает нам объект из Callable, который мы можем использовать для наших собственных задач.

Ниже приведен пример использования экземпляра Callable:

public class CallablesExample{ //Create MyCustomCallable instance List<Future<String>> mFutureList = new ArrayList<Future<String>>(); //Create a list to save the Futures from the Callable Callable<String> mCallable = new MyCustomCallable(); public void main(String args[]){ //Get ExecutorService from Executors utility class, Creating a 5 threads pool. ExecutorService executor = Executors.newFixedThreadPool(5); for (int i = 0; i < 100; i++) { //submit Callable tasks to be executed by thread pool Future<String> future = executor.submit(mCallable); //add Future to the list, we can get return value using Future mFutureList.add(future); } for (Future<String> fut : mFutureList) { try { //Print the return value of Future, Notice the output delay in console

https://riptutorial.com/ru/home 12

Page 15: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

//because Future.get() stop the thread till the task have been completed System.out.println(new Date() + "::" + fut.get()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } //Shut down the service executor.shutdown(); } class MyCustomCallable implements Callable<String> { @Override public String call() throws Exception { Thread.sleep(1000); //return the thread name executing this callable task return Thread.currentThread().getName(); } } }

Как вы можете видеть, мы создаем Threadpool с 5 потоками, это означает, что мы можем бросить 5 callables параллельно. Когда потоки закончатся, мы получим и объект Future из вызываемого, в данном случае имени потока.

ПРЕДУПРЕЖДЕНИЕ

В этом примере мы просто используем фьючерсы как объект внутри массива, чтобы знать, сколько потоков мы выполняем, и печатать много раз консоль журнала с теми данными, которые мы хотим. Но если мы хотим использовать метод Future.get() , чтобы вернуть нам данные, которые мы сохранили ранее в вызываемом, мы заблокируем поток до завершения задачи. Будьте осторожны с такими звонками, когда вы хотите выполнить это как можно быстрее

Пользовательские Runnables вместо Callables

Еще одна хорошая практика для проверки завершения наших потоков без блокирования потока, ожидающего восстановления объекта Future из нашего Callable, - это создать нашу собственную реализацию для Runnables, используя ее вместе с методом execute() .

В следующем примере я показываю пользовательский класс, который реализует Runnable

с внутренним обратным вызовом, с помощью которого мы можем знать, когда исполняемые файлы закончены, и использовать их позже в нашем ThreadPool:

public class CallbackTask implements Runnable { private final Runnable mTask; private final RunnableCallback mCallback; public CallbackTask(Runnable task, RunnableCallback runnableCallback) { this.mTask = task; this.mCallback = runnableCallback; }

https://riptutorial.com/ru/home 13

Page 16: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

public void run() { long startRunnable = System.currentTimeMillis(); mTask.run(); mCallback.onRunnableComplete(startRunnable); } public interface RunnableCallback { void onRunnableComplete(long runnableStartTime); } }

И вот наша реализация ThreadExecutor:

public class ThreadExecutorExample implements ThreadExecutor { private static String TAG = "ThreadExecutorExample"; public static final int THREADPOOL_SIZE = 4; private long mSubmittedTasks; private long mCompletedTasks; private long mNotCompletedTasks; private ThreadPoolExecutor mThreadPoolExecutor; public ThreadExecutorExample() { Log.i(TAG, "[ThreadExecutorImpl] Initializing ThreadExecutorImpl"); Log.i(TAG, "[ThreadExecutorImpl] current cores: " + Runtime.getRuntime().availableProcessors()); this.mThreadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(THREADPOOL_SIZE); } @Override public void execute(Runnable runnable) { try { if (runnable == null) { Log.e(TAG, "[execute] Runnable to execute cannot be null"); return; } Log.i(TAG, "[execute] Executing new Thread"); this.mThreadPoolExecutor.execute(new CallbackTask(runnable, new CallbackTask.RunnableCallback() { @Override public void onRunnableComplete(long RunnableStartTime) { mSubmittedTasks = mThreadPoolExecutor.getTaskCount(); mCompletedTasks = mThreadPoolExecutor.getCompletedTaskCount(); mNotCompletedTasks = mSubmittedTasks - mCompletedTasks; // approximate Log.i(TAG, "[execute] [onRunnableComplete] Runnable complete in " + (System.currentTimeMillis() - RunnableStartTime) + "ms"); Log.i(TAG, "[execute] [onRunnableComplete] Current threads working " + mNotCompletedTasks); } })); } catch (Exception e) { e.printStackTrace();

https://riptutorial.com/ru/home 14

Page 17: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

Log.e(TAG, "[execute] Error, shutDown the Executor"); this.mThreadPoolExecutor.shutdown(); } } } /** * Executor thread abstraction created to change the execution context from any thread from out ThreadExecutor. */ interface ThreadExecutor extends Executor { void execute(Runnable runnable); }

Я сделал этот пример, чтобы проверить скорость моих потоков в миллисекундах, когда они выполнены, без использования Future. Вы можете взять этот пример и добавить его в свое приложение, чтобы контролировать работу параллельной задачи и завершенные / завершенные. Проверка всего момента, время, необходимое для выполнения этих потоков.

Добавление ThreadFactory к Исполнителю

Мы используем ExecutorService для назначения потоков из внутреннего пула потоков или их создания по требованию для выполнения задач. Каждый ExecutorService имеет ThreadFactory, но ExecutorService будет использовать всегда по умолчанию, если мы не настроим его. Почему мы должны это делать?

Чтобы задать более описательное имя потока. По умолчанию ThreadFactory

предоставляет имена потоков в виде пула-m-thread-n, такие как pool-1-thread-1, pool-

2-thread-1, pool-3-thread-1 и т. Д. Если вы пытаетесь отлаживать или контролировать что-то, трудно понять, что это за темы

Задайте настраиваемый статус Daemon, по умолчанию ThreadFactory создает результаты без демона.

Задайте приоритет для наших потоков, по умолчанию ThreadFactory установил средний приоритет для всех своих потоков.

Вы можете указать UncaughtExceptionHandler для нашего потока, используя setUncaughtExceptionHandler() для объекта thread. Это получает обратный вызов, когда метод запуска Thread's генерирует неперехваченное исключение.

Вот простая реализация ThreadFactory поверх ThreadPool.

public class ThreadExecutorExample implements ThreadExecutor { private static String TAG = "ThreadExecutorExample"; private static final int INITIAL_POOL_SIZE = 3; private static final int MAX_POOL_SIZE = 5;

https://riptutorial.com/ru/home 15

Page 18: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

// Sets the amount of time an idle thread waits before terminating private static final int KEEP_ALIVE_TIME = 10; // Sets the Time Unit to seconds private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS; private final BlockingQueue<Runnable> workQueue; private final ThreadPoolExecutor threadPoolExecutor; private final ThreadFactory threadFactory; private ThreadPoolExecutor mThreadPoolExecutor; public ThreadExecutorExample() { this.workQueue = new LinkedBlockingQueue<>(); this.threadFactory = new CustomThreadFactory(); this.threadPoolExecutor = new ThreadPoolExecutor(INITIAL_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, this.workQueue, this.threadFactory); } public void execute(Runnable runnable) { if (runnable == null) { return; } this.threadPoolExecutor.execute(runnable); } private static class CustomThreadFactory implements ThreadFactory { private static final String THREAD_NAME = "thread_"; private int counter = 0; @Override public Thread newThread(Runnable runnable) { return new Thread(runnable, THREAD_NAME + counter++); } } } /** * Executor thread abstraction created to change the execution context from any thread from out ThreadExecutor. */ interface ThreadExecutor extends Executor { void execute(Runnable runnable); }

В этом примере просто измените имя потока с помощью счетчика, но мы можем его изменить до тех пор, пока мы хотим.

Прочитайте Исполнители онлайн: https://riptutorial.com/ru/multithreading/topic/6710/

исполнители

https://riptutorial.com/ru/home 16

Page 19: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

глава 3: Семафоры и мьютексы

Вступление

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

замечания

семафорВот блестящее объяснение этого вопроса Stackoverflow :

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

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

Mutex

Мьютекс - это семафор из 1 (т. Е. Только один поток за раз). Используя метафору ночного клуба, подумайте о мьютексе с точки зрения ванной комнаты в ночном клубе. Одновременно допускался только один человек.

Examples

Mutex в Java и C ++

Хотя Java не имеет класса Mutex, вы можете имитировать Mutex с использованием семафора из 1. Следующий пример выполняет два потока с блокировкой и без них. Без блокировки программа выплевывает несколько случайный порядок выходных символов ($ или #). С блокировкой программа выплескивает симпатичные упорядоченные наборы символов либо #####, либо $$$$$, но никогда не смешивает # и $.

import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadLocalRandom;

https://riptutorial.com/ru/home 17

Page 20: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

public class MutexTest { static Semaphore semaphore = new Semaphore(1); static class MyThread extends Thread { boolean lock; char c = ' '; MyThread(boolean lock, char c) { this.lock = lock; this.c = c; } public void run() { try { // Generate a random number between 0 & 50 // The random nbr is used to simulate the "unplanned" // execution of the concurrent code int randomNbr = ThreadLocalRandom.current().nextInt(0, 50 + 1); for (int j=0; j<10; ++j) { if(lock) semaphore.acquire(); try { for (int i=0; i<5; ++i) { System.out.print(c); Thread.sleep(randomNbr); } } finally { if(lock) semaphore.release(); } System.out.print('|'); } } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws Exception { System.out.println("Without Locking:"); MyThread th1 = new MyThread(false, '$'); th1.start(); MyThread th2 = new MyThread(false, '#'); th2.start(); th1.join(); th2.join(); System.out.println('\n'); System.out.println("With Locking:"); MyThread th3 = new MyThread(true, '$'); th3.start(); MyThread th4 = new MyThread(true, '#'); th4.start(); th3.join(); th4.join(); System.out.println('\n'); }

https://riptutorial.com/ru/home 18

Page 21: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

}

Запустить javac MutexTest.java; java MutexTest , и вы получите что-то вроде этого:

Без блокировки: # $$$$$ | $$$$$ | $$ # $$$ | $$$$$ | $$$$ # $ | $$$$$ | $$$$$ | $ # $$$$ | $$$$$ | $$$ # $$ || ##### | ##### | ##### | ##### | ##### | # #### | ##### | ##### | ##### |

С блокировкой: $$$$$ | ##### | $$$$$ | ##### | $$$$$ | ##### | $$$$$ | ##### | $$$$$ | ##### | $$$$$ | ##### | $$$$$ | ##### | $$$$$ | ##### | $ $$$$ | ##### | $$$$$ | ##### |

Вот такой же пример в C ++:

#include <iostream> // std::cout #include <thread> // std::thread #include <mutex> // std::mutex #include <random> // std::random_device class MutextTest { private: static std::mutex mtx; // mutex for critical section public: static void run(bool lock, char c) { // Generate a random number between 0 & 50 // The random nbr is used to simulate the "unplanned" // execution of the concurrent code std::uniform_int_distribution<int> dist(0, 50); std::random_device rd; int randomNbr = dist(rd); //std::cout << randomNbr << '\n'; for(int j=0; j<10; ++j) { if(lock) mtx.lock(); for (int i=0; i<5; ++i) { std::cout << c << std::flush; std::this_thread::sleep_for(std::chrono::milliseconds(randomNbr)); } std::cout << '|'; if(lock) mtx.unlock(); } } }; std::mutex MutextTest::mtx; int main() { std::cout << "Without Locking:\n"; std::thread th1 (MutextTest::run, false, '$'); std::thread th2 (MutextTest::run, false, '#'); th1.join(); th2.join(); std::cout << "\n\n";

https://riptutorial.com/ru/home 19

Page 22: multithreading - RIP Tutorial · • Легко определять приоритеты определенных операций над другими ... Различные типы

std::cout << "With Locking:\n"; std::thread th3 (MutextTest::run, true, '$'); std::thread th4 (MutextTest::run, true, '#'); th3.join(); th4.join(); std::cout << '\n'; return 0; }

Запустить g++ --std=c++11 MutexTest.cpp; ./a.out , и вы получите что-то вроде этого:

Без блокировки: $ # $ # $ # $ # $ # | $ | # $ # $ # $ # $ # | $$ | # $ # $ # $ # | $ # $ | # $ # $ # $ # | $ $ # $ | # $ # $ # | $ # $ # $ | # $ # $ # | $ # $$ # $ | # $ # | $ # $ # $ # $ | # $ # | $ # $ # $ # $$ | # | $ # $ # $ # $ # $ | # | #### |

С блокировкой: $$$$$ | ##### | $$$$$ | ##### | $$$$$ | ##### | $$$$$ | ##### | $$$$$ | ##### | $$$$$ | ##### | $$$$$ | ##### | $$$$$ | ##### | $ $$$$ | ##### | $$$$$ | ##### |

Прочитайте Семафоры и мьютексы онлайн: https://riptutorial.com/ru/multithreading/topic/10861/семафоры-и-мьютексы

https://riptutorial.com/ru/home 20