concurrency java semaphore
У цьому підручнику будуть розглянуті компоненти пакету java.util.concurrent, такі як Java Semaphore, Executor Framework, ExecutorService для реалізації Concurrency в Java:
З наших попередніх підручників Java ми знаємо, що платформа Java підтримує паралельне програмування з нуля. Основною одиницею одночасності є потік, і ми детально обговорили потоки та багатопоточність у Java.
Починаючи з Java 5, на платформу Java був доданий пакет під назвою «java.util.concurrent». Цей пакет містить набір класів і бібліотек, що полегшує програмісту розробку одночасних (багатопотокових) додатків. Використовуючи цей пакет, нам не потрібно писати складні класи, оскільки ми маємо готові реалізації більшості одночасних концепцій.
=> Перевірте ВСІ підручники Java тут.
У цьому посібнику ми обговоримо різні компоненти пакету java.util.concurrent щодо паралельності та багатопоточності в Java.
Що ви дізнаєтесь:
java.util.concurrent Package
Нижче наведено різні компоненти пакету java.util.concurrent, що стосуються паралельності та багатопоточності в Java. Давайте детально вивчимо кожен компонент за допомогою простих прикладів програмування. Деякі компоненти ми будемо
обговорити:
- Каркас виконавця
- Виконавець
- ThreadPool
- Викличний
- Замки - ReentrantLock
- Семафор
- ForkJoinPool
Виконавець Framework в Java
Виконавець Framework в Java був випущений з випуском JDK 5. Фреймворк виконавця (java.util.concurrent.Executor) - це фреймворк, який складається з компонентів, які допомагають нам ефективно обробляти кілька потоків.
Використовуючи Executor Framework, ми можемо запускати об’єкти, які можна запустити, повторно використовуючи вже існуючі потоки. Нам не потрібно створювати нові потоки кожного разу, коли нам потрібно запускати об'єкти.
Виконавець API відокремлює або відключає виконання завдання від фактичного завдання за допомогою Виконавець . Виконавець зосереджений на інтерфейсі виконавця і має підінтерфейси, тобто Виконавець і клас ThreadPoolExeecuter.
Таким чином, використовуючи Executor, нам просто потрібно створити Runnable об'єкти, а потім відправити їх виконавцю, який їх виконує.
Одними з найкращих практик, яких слід дотримуватися під час використання фреймворку Executor, є,
- Нам слід перехресно перевірити та спланувати код для перегляду найкращих списків, щоб ми могли виявити тупикові ситуації, а також відсутність у коді.
- Код Java завжди повинен виконуватися за допомогою інструментів статичного аналізу. Приклади інструментами статичного аналізу є FindBugs та PMD.
- Ми повинні ловити не лише винятки, але й помилки в багатопотокових програмах.
Тепер обговоримо компоненти Executor Framework у Java.
Виконавець
Виконавець може бути визначений як інтерфейс, який використовується для представлення об'єкта, який виконує надані йому завдання. Чи буде завдання виконуватися в поточному чи новому потоці, залежить від точки, з якої було ініційовано виклик, що далі залежить від реалізації.
Отже, використовуючи Executor, ми можемо від’єднати завдання від фактичного завдання, а потім виконати їх асинхронно.
Однак виконання завдання за допомогою Executor не обов'язково має бути асинхронним. Виконавці також можуть миттєво викликати завдання за допомогою викликаючого потоку.
Нижче наведено приклад шматка коду для створення екземпляра Executor:
public class Invoker implements Executor { @Override public void execute (Runnable r_interface) { r_interface.run(); } }
Після створення засобу виклику, як показано вище, ми можемо використовувати його для виконання завдання наступним чином.
public void execute () { Executor executor = new Invoker (); executor.execute ( () -> { //perform task }); }
Зверніть увагу, що якщо завдання не прийнято Виконавцем, воно викидає RejectedExecutionException.
Виконавець
ExecutorService (java.util.concurrent.ExecutorService) планує подані завдання відповідно до наявності потоків, а також підтримує чергу пам'яті. ExecutorService виступає як комплексне рішення для асинхронної обробки завдань.
Щоб використовувати ExecutorService в коді, ми створюємо клас Runnable. ExecutorService підтримує пул потоків, а також призначає завдання потокам. Завдання також можуть стояти в черзі, якщо потік недоступний.
Нижче наведено простий приклад ExecutorService.
import java.util.concurrent.*; public class Main { public static void main(String() args) { //create ExecutorService instance with 10 threads ExecutorService executor_Service = Executors.newFixedThreadPool(10); //assign the service to Runnable instance executor_Service.execute(new Runnable() { @Override public void run() { //print the message System.out.println('Simple Example of ExecutorService!!!'); } }); //shutdown executorService executor_Service.shutdown(); } }
Вихідні дані
У наведеній вище програмі ми створюємо простий екземпляр ExecutorService з пулом потоків, що складається з 10 потоків. Потім він призначається виконуваному екземпляру та виконується для друку вищевказаного повідомлення. Після друку повідомлення ExecutorService вимикається.
Басейн ниток
Пул потоків на Java - це група робочих потоків, яку можна багаторазово використовувати повторно та призначити завдання.
Пул потоків містить групу потоків фіксованого розміру. Кожен потік витягується з пулу потоків і йому призначається завдання постачальником послуг. Після завершення призначеного завдання потік знову передається в пул потоків.
Пул потоків є вигідним, оскільки нам не потрібно створювати новий потік кожного разу, коли завдання доступне, тим самим підвищується продуктивність. Він використовується в додатках реального часу, які використовують Servlet і JSP, де пули потоків використовуються для обробки запитів.
У багатопотокових програмах пул потоків економить ресурси та допомагає стримати паралелізм у визначених межах.
Наведена нижче програма Java демонструє пул потоків у Java.
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; class WorkerThreadClass implements Runnable { private String message; //thread class constructor public WorkerThreadClass(String s){ this.message=s; } //run method for thread public void run() { System.out.println(' Start: '+message); processmessage(); //sleep between start and end System.out.println(' End: '+ message); } //processmessage method => sleeps the thread for 2 sec private void processmessage() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } public class Main { public static void main(String() args) { //create a ExecutorService instance ExecutorService executor = Executors.newFixedThreadPool(5);//creating a pool of 5 threads //create thread instances and execute them for (int i = 0; i <5; i++) { Runnable workerThrd = new WorkerThreadClass('Thread_' + i); executor.execute(workerThrd);//calling execute method of ExecutorService } //shutdown ExecutorService executor.shutdown(); while (!executor.isTerminated()) { } System.out.println('Finished all threads'); } }
Вихідні дані
У вищевказаних програмах є пул потоків із 5 потоків, які створені методом “newFixedThreadPool”. Потім потоки створюються та додаються до пулу та призначаються службі ExecutorService для виконання.
де дивитись аніме безкоштовно
Викликається в Java
Ми вже знаємо, що можемо створювати потоки, використовуючи два підходи. Один із підходів - розширення класу Thread, а другий - реалізація інтерфейсу Runnable.
Однак потоки, створені за допомогою інтерфейсу Runnable, не мають однієї функції, тобто він не повертає результат, коли потік завершується або run () завершує виконання. Ось тут і з’являється інтерфейс Callable.
За допомогою інтерфейсу Callable ми визначаємо завдання так, щоб воно повертало результат. Це також може спричинити виняток. Викличний інтерфейс є частиною пакету java.util.concurrent.
Інтерфейс Callable забезпечує метод call (), який знаходиться в подібних рядках, як метод run (), який надається інтерфейсом Runnable, з тією лише різницею, що метод call () повертає значення та видає перевірене виняток.
Метод call () інтерфейсу Callable має такий прототип.
public Object call () throws Exception;
Оскільки метод call () повертає Object, основний потік повинен це знати.
Отже, повернене значення повинно зберігатися в іншому об'єкті, відомому головному потоку. Цій меті слугує використання об’єкта “Майбутнє”. Майбутній об'єкт - це об'єкт, що містить результат, повернутий потоком. Або іншими словами, він буде містити результат, коли Callable повернеться.
Callable інкапсулює завдання, яке повинно виконуватися в іншому потоці. Об’єкт майбутнього зберігає результат, повернутий з іншого потоку.
Викличний інтерфейс не може використовуватися для створення потоку. Нам потрібен Runnable для створення потоку. Потім для збереження результату потрібен об'єкт Future. Java пропонує конкретний тип під назвою «FutureTask», який поєднує в собі функціональність, реалізуючи як Runnable, так і Future.
Ми створюємо FutureTask, надаючи конструктору Callable. Потім цей об’єкт FutureTask передається конструктору класу Thread для створення об’єкта Thread.
Нижче наведено програму Java, яка демонструє інтерфейс Callable та об'єкт Future. У цій програмі ми також використовуємо об’єкт FutureTask.
Як уже зазначалося, у програмі ми створюємо клас, який реалізує інтерфейс Callable з перевизначеним методом call (). В основному методі ми створюємо 10 об’єктів FutureTask. Кожен конструктор об'єктів має в якості аргументу об'єкт класу Callable. Тоді об'єкт FutureTask асоціюється з екземпляром потоку.
Отже, побічно ми створюємо потік, використовуючи об'єкт інтерфейсу Callable.
import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; //create a class implementing Callable interface class CallableDemo implements Callable { //define call () method public Object call() throws Exception { Random generator = new Random(); Integer randomNumber = generator.nextInt(10); Thread.sleep(randomNumber * 1000); return randomNumber; } } public class Main { public static void main(String() args) throws Exception { // Array of FutureTask objects FutureTask() randomNumberTasks = new FutureTask(10); for (int i = 0; i <10; i++) { Callable callable = new CallableDemo(); // Create the FutureTask with Callable class randomNumberTasks(i) = new FutureTask(callable); // create thread with FutureTask Thread t = new Thread(randomNumberTasks(i)); //start the thread t.start(); } System.out.println('The contents of FutureTask objects:'); for (int i = 0; i < 10; i++) { // get() contents of FutureTask System.out.print(randomNumberTasks(i).get() + ' '); } } }
Вихідні дані
Як показано у наведеній вище програмі, метод call () Callable, який замінений у класі, що реалізує Callable, генерує випадкові числа. Після запуску потоку він відображає ці випадкові числа.
Крім того, ми використовуємо об'єкти FutureTask в основній функції. Оскільки він реалізує інтерфейс Future, нам не потрібно зберігати результати в об’єктах Thread. Подібним чином ми можемо скасувати завдання, перевірити, чи воно запущене чи завершене, а також отримати результат за допомогою об’єкта FutureTask.
ReentrantLock в Java
Ми детально обговорили синхронізацію потоків із використанням ключового слова синхронізації у нашому останньому підручнику. Використання синхронізованого слова для потокової синхронізації є основним методом і є дещо жорстким.
Використовуючи синхронізоване ключове слово, потік може заблокувати лише один раз. Крім того, після того, як один потік виходить із синхронізованого блоку, наступний потік приймає блокування. Черги очікування немає. Ці проблеми можуть спричинити голодування деяких інших потоків, оскільки вони можуть не мати доступу до ресурсів протягом тривалого часу.
Для вирішення цих проблем нам потрібен гнучкий метод синхронізації потоків. „Блокування реентрантів” - це метод у Java, який забезпечує синхронізацію з набагато більшою гнучкістю.
Клас “ReentrantLock” реалізує блокування Reentrant і є частиною пакету “import java.util.concurrent.locks”. Клас ReentrantLock забезпечує синхронізацію методу для доступу до спільних ресурсів. Класи також мають методи блокування та розблокування для блокування / розблокування ресурсів при доступі до потоків.
Особливістю ReentrantLock є те, що потік може блокувати спільний ресурс кілька разів за допомогою ReentrantLock. Він забезпечує кількість утримувань, яка встановлюється на одиницю, коли потік блокує ресурс.
Потік може повторно увійти та отримати доступ до ресурсу перед розблокуванням. Щоразу, коли потік отримує доступ до ресурсу за допомогою блокування Reentrant, кількість утримувань збільшується на одиницю. Для кожного розблокування кількість утримувань зменшується на одиницю.
Коли кількість затримок досягне 0, спільний ресурс розблокується.
Клас ReentrantLock також надає параметр справедливості, який є логічним значенням, яке можна передавати за допомогою конструктора блокування. Коли для параметра справедливості встановлено значення true, тоді, коли один потік відпускає замок, блокування передається найдовшому потоку очікування. Це запобігає голодуванню.
Замки Reentrant можна використовувати наступним чином:
return_type method_name() { reentrantlock.lock(); try { //Do some work } catch(Exception e) { e.printStackTrace(); } finally { reentrantlock.unlock(); } }
Зверніть увагу, що оператор розблокування для ReentrantLock завжди знаходиться в блоці нарешті. Це гарантує вивільнення блокування, навіть у випадку виникнення винятку.
Давайте реалізуємо програму Java, щоб зрозуміти ReentrantLock.
import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.*; import java.util.concurrent.locks.ReentrantLock; //thread class that implements Runnable interface class ThreadClass implements Runnable { String task_name; //define ReentrantLock object ReentrantLock thrd_lck; //ThreadClass constructor initialized lock and task name public ThreadClass(ReentrantLock r_lock, String t_name) { thrd_lck = r_lock; task_name = t_name; } //thread run () method public void run() { boolean bool_val = false; while (!bool_val) { //check for Outer Lock boolean tryLock_val = thrd_lck.tryLock(); // if lock is free, do the following if(tryLock_val) { try { for(int i=0;i<=6;i++) { if(i>=2) { thrd_lck.lock(); Thread thread_one = new Thread(); System.out.println('Thread Created.....'); if(i==3) { thread_one.setName('Maint Thread2'); System.out.println('Thread Created.....'); } } if(i==4) thrd_lck.unlock(); break; } System.out.println('ReentrantLock=>Is locked after sleep(1500) : ' + thrd_lck.isLocked()); System.out.println('Work done for task : ' + task_name ); bool_val = true; } catch(Exception e) { e.printStackTrace(); } } } } } public class Main { public static void main(String() args) { //define ReentrantLock lock object and service pool ReentrantLock reentrant_lock = new ReentrantLock(); ExecutorService pool = Executors.newFixedThreadPool(2); //create thread instance and pass lock and task name Runnable worker_thread = new ThreadClass(reentrant_lock, 'ThreadJob'); //execute the thread in exec pool pool.execute(worker_thread); //shut down the pool pool.shutdown(); } }
Вихідні дані
У наведеній вище програмі ми створили потік і використали для нього ReentrantLock. За допомогою ReentrantLock можна отримати доступ до спільного ресурсу.
Семафор на Java
Наступним методом потокової синхронізації є використання Semaphore. За допомогою цієї конструкції, званої семафором, доступ до спільного ресурсу контролюється за допомогою лічильника. Сигнали передаються між потоками, щоб ми могли захищати критичну секцію, а також уникати пропущених сигналів.
Семафор можна визначити як змінну, яка використовується для управління паралельними процесами шляхом синхронізації цих процесів. Семафори також використовуються для синхронізації доступу до спільного ресурсу і тим самим уникають перегонових умов. Дозвіл, наданий потоку для доступу до спільного ресурсу семафором, також називається дозволом.
Залежно від того, які функції вони виконують, семафори можна розділити на два типи:
# 1) Двійковий семафор: Бінарний семафор використовується для синхронізації паралельних процесів та здійснення взаємного виключення. Двійковий семафор приймає лише два значення, тобто 0 і 1.
# 2) Підрахунок семафору: Лічильний семафор має значення, яке вказує кількість процесів, які можуть увійти до критичного розділу. У будь-який момент значення вказує максимальну кількість процесів, які входять у критичний розділ.
То як працює семафор?
Робота семафору може бути підсумована на наступних етапах:
- Якщо кількість семафорів> 0, це означає, що потік має дозвіл на доступ до критичної секції, а потім кількість зменшується.
- В іншому випадку нитка блокується до отримання дозволу.
- Коли потік закінчується з доступом до спільного ресурсу, дозвіл звільняється і кількість семафорів збільшується, щоб інший потік міг повторити вищезазначені кроки та отримати дозвіл.
Вищезазначені етапи роботи семафорів можуть бути зведені в нижченаведеній блок-схемі.
У Java нам не потрібно реалізовувати наш семафор, але він надає файл Семафор клас, який реалізує функціональність семафору. Клас Semaphore є частиною java.util.concurrent пакет.
Клас Semaphore надає такі конструктори, за допомогою яких ми можемо створити об'єкт семафору:
Semaphore (int num_value) Semaphore (int num_value, boolean how)
Ось,
Як передавати масив як параметр в Java
число_значення => початкове значення кількості дозволів, яке визначає кількість потоків, які можуть отримати доступ до спільного ресурсу.
як => встановлює порядок надання дозволів потокам (як = істина). Якщо як = false, то такого порядку не дотримується.
Тепер ми реалізуємо програму Java, яка продемонструє семафор, який використовується для управління загальним доступом до ресурсів та запобігання перегоновим умовам.
import java.util.concurrent.*; //class for shared resource class SharedRes { static int count = 0; } class ThreadClass extends Thread { Semaphore sem; String threadName; public ThreadClass(Semaphore sem, String threadName) { super(threadName); this.sem = sem; this.threadName = threadName; } @Override public void run() { // Thread T1 processing if(this.getName().equals('T1')) { System.out.println('Start: ' + threadName); try { System.out.println(threadName + ' :waiting for a permit.'); // acquire the permit sem.acquire(); System.out.println(threadName + ':Acquired permit'); // access shared resource for(int i=0; i <5; i++) { SharedRes.count++; System.out.println(threadName + ': ' + SharedRes.count); Thread.sleep(10); } } catch (InterruptedException exc) { System.out.println(exc); } // Release the permit. System.out.println(threadName + ':Released the permit'); sem.release(); } // Thread T2 processing else { System.out.println('Start: ' + threadName); try { System.out.println(threadName + ':waiting for a permit.'); // acquire the lock sem.acquire(); System.out.println(threadName + ':Acquired permit'); // process the shared resource for(int i=0; i < 5; i++) { SharedRes.count--; System.out.println(threadName + ': ' + SharedRes.count); Thread.sleep(10); } } catch (InterruptedException exc) { System.out.println(exc); } // Release the permit. System.out.println(threadName + ':Released the permit.'); sem.release(); } } } public class Main { public static void main(String args()) throws InterruptedException { //create Semaphore=> #permits = 1 Semaphore sem = new Semaphore(1); // Create thread instances T1 & T2 //T1=> Increments the count; T2=> Decrements the count ThreadClass thread1 = new ThreadClass(sem, 'T1'); ThreadClass thread2 = new ThreadClass(sem, 'T2'); // start T1 & T2 thread1.start(); thread2.start(); // Wait T1 & T2 thread1.join(); thread2.join(); System.out.println('count: ' + SharedRes.count); // display final count. } }
Вихідні дані
Ця програма оголосила клас для спільного ресурсу. Він також оголошує клас потоку, в якому ми маємо змінну семафору, яка ініціалізується в конструкторі класу.
У методі overridden run () класу Thread виконується обробка екземпляра потоку, коли потік отримує дозвіл, отримує доступ до спільного ресурсу, а потім звільняє дозвіл.
В основному методі ми оголосили два екземпляри потоків. Потім обидва потоки запускаються, а потім вони чекають за допомогою методу join. Нарешті, відображається кількість, тобто 0, що вказує на те, що обидва потоки закінчили спільний ресурс.
Вилка та приєднання до Java
Вперше фреймворк fork / join був представлений в Java 7. Цей фреймворк складається з інструментів, які можуть прискорити паралельну обробку. Він використовує всі доступні в системі ядра процесора і виконує завдання. Фреймворк fork / join використовує підхід розділити та завоювати.
Основна ідея фреймворку Fork / Join полягає в тому, що перший фреймворк “Forks”, тобто рекурсивно розбиває завдання на менші окремі підзадачі, поки завдання не стануть атомарними, щоб їх можна було виконувати асинхронно.
Після цього завдання “об’єднуються”, тобто всі підзадачі об’єднуються рекурсивно в одне завдання або повертається значення.
Фреймворк fork / join має пул потоків, відомий як “ForkJoinPool”. Цей пул управляє робочими потоками типу “ForkJoinWorkerThread”, забезпечуючи тим самим ефективну паралельну обробку.
ForkJoinPool керує робочими потоками, а також допомагає нам отримувати інформацію щодо продуктивності та стану пулу потоків. ForkJoinPool - це реалізація “ExecutorService”, про яку ми вже говорили вище.
На відміну від робочих потоків, ForkJoinPool не створює окремий потік для кожної підзадачі. Кожен потік у ForkJoinPool зберігає свій дек (двостороння черга) для зберігання завдань.
Deque виконує функцію балансування навантаження потоку і робить це за допомогою «алгоритму викрадення роботи», який описаний нижче.
Алгоритм викрадення роботи
Ми можемо визначити алгоритм викрадення роботи простими словами як 'Якщо потік вільний,' викрадайте 'роботу із зайнятих ниток'.
Робочий потік завжди отримуватиме завдання зі свого завдання. Коли всі завдання в deque вичерпані, а deque порожній, робочий потік прийме завдання з хвоста іншого deque або з «глобальної черги входу».
Таким чином зводиться до мінімуму можливість змагання ниток за завдання, а також зменшується кількість випадків, коли потік повинен шукати роботу. Це тому, що нитка вже отримала найбільший шматок доступної роботи і закінчила її.
Тож як ми можемо використовувати ForkJoinPool у програмі?
Загальне визначення ForkJoinPool є таким:
public class ForkJoinPool extends AbstractExecutorService
Клас ForkJoinPool є частиною пакету “java.util.concurrent”.
У Java 8 ми створюємо екземпляр ForkJoinPool, використовуючи його статичний метод “common-pool ()”, який забезпечує посилання на загальний пул або пул потоків за замовчуванням.
ForkJoinPool commonPool = ForkJoinPool.commonPool ();
У Java 7 ми створюємо екземпляр ForkJoinPool і призначаємо його полю класу утиліти, як показано нижче.
public static ForkJoinPool forkJoinPool = new ForkJoinPool(2);
Наведене вище визначення вказує на те, що пул має рівень паралельності 2, такий що пул буде використовувати 2 ядра процесора.
Щоб отримати доступ до вищезазначеного пулу, ми можемо дати таке твердження.
ForkJoinPool forkJoinPool = PoolUtil.forkJoinPool;
Базовим типом завдань ForkJoinPool є “ForkJoinTask”. Ми повинні розширити один з його підкласів, тобто для порожніх завдань, RecursiveAction та для завдань, що повертають значення, RecursiveTask. Обидва розширені класи забезпечують абстрактний метод compute (), в якому ми визначаємо логіку завдання.
Нижче наведено приклад для демонстрації ForkJoinPool.
import java.util.ArrayList; import java.util.List; import java.util.concurrent.*; //class declaration for ForkJoinPool tasks class FJPoolTask extends RecursiveAction { private long Load = 0; public FJPoolTask(long Load) { this.Load = Load; } @Override protected void compute() { //if threshold is reached, break tasks into smaller tasks List subtasks = new ArrayList(); subtasks.addAll(createSubtasks()); for(RecursiveAction subtask : subtasks){ subtask.fork(); } } //create subtasks private List createSubtasks() { List sub_tasks =new ArrayList(); FJPoolTask sub_task1 = new FJPoolTask(this.Load / 2); FJPoolTask sub_task2 = new FJPoolTask(this.Load / 2); FJPoolTask sub_task3 = new FJPoolTask(this.Load / 2); sub_tasks.add(sub_task1); sub_tasks.add(sub_task2); sub_tasks.add(sub_task3); return sub_tasks; } } public class Main { public static void main(final String() arguments) throws InterruptedException { //get count of available processors int proc = Runtime.getRuntime().availableProcessors(); System.out.println('Processors available:' +proc); //declare forkJoinPool ForkJoinPool Pool = ForkJoinPool.commonPool(); System.out.println(' Active Threads (Before invoke):' +Pool.getActiveThreadCount()); //Declare ForkJoinPool task object FJPoolTask t = new FJPoolTask(400); //submit the tasks to the pool Pool.invoke(t); System.out.println(' Active Threads (after invoke):' +Pool.getActiveThreadCount()); System.out.println('Common Pool Size :' +Pool.getPoolSize()); } }
Вихідні дані
У наведеній вище програмі ми знаходимо кількість активних потоків у системі до та після виклику методу “invoke ()”. Метод invoke () використовується для надсилання завдань до пулу. Ми також знаходимо кількість доступних процесорних ядер в системі.
Часті запитання
Q # 1) Що таке Java Util Concurrent?
Відповідь: Пакет 'java.util.concurrent' - це набір класів та інтерфейсів, що надаються Java для полегшення розробки одночасних (багатопотокових) додатків. Використовуючи цей пакет, ми можемо безпосередньо використовувати інтерфейс та класи, а також API, без необхідності писати наші класи.
Q # 2) Які з наведених нижче є одночасними реалізаціями, присутніми в java.util. одночасний пакет?
Відповідь: На високому рівні пакет java.util.concurrent містить такі утиліти, як виконавці, синхронізатори, черги, таймінги та паралельні колекції.
Q # 3) Що таке майбутня Java?
Відповідь: Майбутній об’єкт (java.util.concurrent.Future) використовується для зберігання результату, повернутого потоком, коли реалізований інтерфейс Callable.
Q # 4) Що є безпечним для потоків у Java?
як відтворювати файли .bin на ПК
Відповідь: Потокобезпечний код або клас у Java - це код або клас, якими можна спільно користуватися в багатопотоковому або одночасному середовищі без будь-яких проблем і дає очікувані результати.
Q # 5) Що таке синхронізована колекція в Java?
Відповідь: Синхронізована колекція - це безпечна для потоків колекція. Метод синхронізованої колекції () класу java.util.Collections повертає синхронізовану (безпечну для потоків) колекцію.
Висновок
За допомогою цього підручника ми завершили тему багатопоточності та паралельності в Java. Ми детально обговорили багатопоточність у наших попередніх навчальних посібниках. Тут ми обговорили паралельність та реалізацію, пов’язану з паралельністю та багатопоточністю, які є частиною пакету java.util.concurrent.
Ми обговорили ще два методи синхронізації, семафори та ReentrantLock. Ми також обговорили ForkJoinPool, який використовується для виконання завдань, розділивши їх на більш прості завдання, а потім зрештою приєднавши результат.
Пакет java.util.concurrent також підтримує фреймворк Executor та виконавці, які допомагають нам виконувати потоки. Ми також обговорили реалізацію пулу потоків, що складається з багаторазових потоків, які повертаються до пулу після завершення виконання.
Ми обговорили інший інтерфейс, схожий на Runnable, який також допомагає нам повернути результат з потоку та об'єкт Future, який використовується для зберігання отриманого результату потоку.
=> Зверніть увагу на прості навчальні серії Java тут.
Рекомендована література
- Thread.Sleep () - метод Thread Sleep () у Java із прикладами
- Розгортання Java: створення та виконання файлу Java JAR
- Основи Java: Синтаксис Java, клас Java та основні концепції Java
- Віртуальна машина Java: як JVM допомагає у запуску програми Java
- Модифікатори доступу в Java - Підручник із прикладами
- Java Synchronized: Що таке синхронізація потоків у Java
- Підручник JAVA для початківців: 100+ практичних навчальних посібників Java
- Java Integer та клас Java BigInteger з прикладами