concurrency java semaphore
이 자습서에서는 Java에서 동시성을 구현하기위한 Java Semaphore, Executor Framework, ExecutorService와 같은 java.util.concurrent 패키지의 구성 요소에 대해 설명합니다.
이전 Java 튜토리얼에서 Java 플랫폼이 처음부터 동시 프로그래밍을 지원한다는 것을 알고 있습니다. 동시성의 기본 단위는 스레드이며 Java의 스레드 및 멀티 스레딩에 대해 자세히 설명했습니다.
Java 5부터는 'java.util.concurrent'라는 패키지가 Java 플랫폼에 추가되었습니다. 이 패키지에는 프로그래머가 동시 (다중 스레드) 응용 프로그램을보다 쉽게 개발할 수 있도록하는 일련의 클래스와 라이브러리가 포함되어 있습니다. 이 패키지를 사용하면 대부분의 동시 개념을 즉시 구현할 수 있으므로 복잡한 클래스를 작성할 필요가 없습니다.
이 튜토리얼에서는 Java의 동시성 및 멀티 스레딩과 관련된 java.util.concurrent 패키지의 다양한 구성 요소에 대해 설명합니다.
학습 내용 :
java.util.concurrent 패키지
다음은 Java의 동시성 및 다중 스레딩과 관련된 java.util.concurrent 패키지의 다양한 구성 요소입니다. 간단한 프로그래밍 예제를 사용하여 각 구성 요소를 자세히 살펴 보겠습니다. 일부 구성 요소는
토론은 다음과 같습니다.
- 실행자 프레임 워크
- ExecutorService
- ThreadPool
- 호출 가능
- 잠금-재진입 잠금
- 신호기
- ForkJoinPool
Java의 실행자 프레임 워크
Java의 Executor Framework는 JDK 5 릴리스와 함께 릴리스되었습니다. Executor Framework (java.util.concurrent.Executor)는 여러 스레드를 효율적으로 처리하는 데 도움이되는 구성 요소로 구성된 프레임 워크입니다.
Executor Framework를 사용하면 이미 존재하는 스레드를 재사용하여 Runnable 객체를 실행할 수 있습니다. 객체를 실행해야 할 때마다 새 스레드를 만들 필요는 없습니다.
Executor API는 다음을 사용하여 실제 작업에서 작업 실행을 분리하거나 분리합니다. 집행자 . 실행자는 Executor 인터페이스의 중심에 있으며 하위 인터페이스가 있습니다. ExecutorService 그리고 수업 ThreadPoolExecutor.
따라서 Executor를 사용하여 Runnable 객체를 생성 한 다음이를 실행하는 실행기로 보내면됩니다.
Executor 프레임 워크를 사용하는 동안 따라야 할 몇 가지 모범 사례는 다음과 같습니다.
- 코드에서 교착 상태와 라이브 록을 감지 할 수 있도록 상위 목록을 검토하는 코드를 비교 검토하고 계획해야합니다.
- Java 코드는 항상 정적 분석 도구에 대해 실행되어야합니다. 예 정적 분석 도구 중에는 FindBugs 및 PMD가 있습니다.
- 예외뿐만 아니라 다중 스레드 프로그램의 오류도 포착해야합니다.
이제 Java에서 Executor Framework의 구성 요소에 대해 설명하겠습니다.
집행자
실행 프로그램은 제공된 작업을 실행하는 개체를 나타내는 데 사용되는 인터페이스로 정의 할 수 있습니다. 태스크가 현재 스레드 또는 새 스레드에서 실행되는지 여부는 호출이 시작된 지점에 따라 달라지며 구현에 따라 다릅니다.
따라서 Executor를 사용하여 실제 작업에서 작업을 분리 한 다음 비동기 적으로 실행할 수 있습니다.
그러나 Executor를 사용한 태스크 실행은 비동기적일 필요는 없습니다. 실행자는 또한 호출 스레드를 사용하여 작업을 즉시 호출 할 수 있습니다.
안드로이드를위한 최고의 무료 mp3 음악 다운로더 앱
다음은 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 }); }
작업이 Executor에 의해 수락되지 않으면 RejectedExecutionException이 발생합니다.
ExecutorService
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(); } }
산출
위 프로그램에서는 10 개의 스레드로 구성된 스레드 풀이있는 간단한 ExecutorService 인스턴스를 만듭니다. 그런 다음 Runnable 인스턴스에 할당되고 실행되어 위의 메시지를 인쇄합니다. 메시지를 인쇄 한 후 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'); } }
산출
위의 프로그램에는 'newFixedThreadPool'메소드를 사용하여 생성 된 5 개의 스레드로 구성된 스레드 풀이 있습니다. 그런 다음 스레드가 생성되어 풀에 추가되고 실행을 위해 ExecutorService에 할당됩니다.
자바에서 호출 가능
우리는 이미 두 가지 접근 방식을 사용하여 스레드를 만들 수 있다는 것을 알고 있습니다. 한 가지 접근 방식은 Thread 클래스를 확장하는 것이고 두 번째 접근 방식은 Runnable 인터페이스를 구현하는 것입니다.
그러나 Runnable 인터페이스를 사용하여 생성 된 스레드에는 한 가지 기능이 없습니다. 즉, 스레드가 종료되거나 실행 () 실행이 완료 될 때 결과를 반환하지 않습니다. 이것이 Callable 인터페이스가 등장하는 곳입니다.
Callable 인터페이스를 사용하여 결과를 반환하도록 작업을 정의합니다. 예외가 발생할 수도 있습니다. Callable 인터페이스는 java.util.concurrent 패키지의 일부입니다.
Callable 인터페이스는 call () 메서드가 값을 반환하고 확인 된 예외를 throw한다는 유일한 차이점을 제외하고 Runnable 인터페이스에서 제공하는 run () 메서드와 유사한 줄에있는 call () 메서드를 제공합니다.
Callable 인터페이스의 call () 메소드는 다음과 같은 프로토 타입을 가지고 있습니다.
public Object call () throws Exception;
call () 메서드는 Object를 반환하므로 주 스레드는이를 알고 있어야합니다.
따라서 반환 값은 메인 스레드에 알려진 다른 객체에 저장되어야합니다. 이 목적은 '미래'개체를 사용하여 제공됩니다. Future 객체는 스레드가 반환 한 결과를 보유하는 객체입니다. 즉, Callable이 돌아올 때 결과를 보유합니다.
Callable은 다른 스레드에서 실행되어야하는 작업을 캡슐화합니다. Future 객체는 다른 스레드에서 반환 된 결과를 저장합니다.
Callable 인터페이스는 스레드를 만드는 데 사용할 수 없습니다. 스레드를 생성하려면 Runnable이 필요합니다. 그런 다음 결과를 저장하려면 Future 객체가 필요합니다. Java는 Runnable과 Future를 모두 구현하여 기능을 결합한 'FutureTask'라는 구체적인 유형을 제공합니다.
Callable에 생성자를 제공하여 FutureTask를 만듭니다. 이 FutureTask 객체는 Thread 객체를 생성하기 위해 Thread 클래스의 생성자에 제공됩니다.
다음은 Callable 인터페이스와 Future 객체를 보여주는 Java 프로그램입니다. 우리는 또한이 프로그램에서 FutureTask 객체를 사용합니다.
이미 언급했듯이 프로그램에서 재정의 된 call () 메서드로 Callable 인터페이스를 구현하는 클래스를 만듭니다. 주요 방법에서는 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() + ' '); } } }
산출
위의 프로그램에서 보듯이 Callable을 구현하는 클래스에서 재정의 된 Callable의 call () 메서드는 난수를 생성합니다. 스레드가 시작되면 이러한 난수를 표시합니다.
또한 main 함수에서 FutureTask 객체를 사용합니다. Future 인터페이스를 구현하기 때문에 결과를 Thread 객체에 저장할 필요가 없습니다. 마찬가지로 작업을 취소하고 실행 중인지 또는 완료되었는지 확인하고 FutureTask 개체를 사용하여 결과를 가져올 수도 있습니다.
Java의 ReentrantLock
지난 튜토리얼에서 동기화 된 키워드를 사용한 스레드 동기화에 대해 자세히 설명했습니다. 스레드 동기화를 위해 동기화 된 단어를 사용하는 것은 기본 방법이며 다소 엄격합니다.
동기화 된 키워드를 사용하면 스레드는 한 번만 잠글 수 있습니다. 또한 한 스레드가 동기화 된 블록을 종료 한 후 다음 스레드가 잠금을받습니다. 대기 대기열이 없습니다. 이러한 문제는 오랫동안 리소스에 액세스하지 못할 수 있으므로 다른 스레드의 기아를 유발할 수 있습니다.
이러한 문제를 해결하려면 스레드를 동기화하는 유연한 방법이 필요합니다. '재진입 잠금'은 훨씬 더 유연한 동기화를 제공하는 Java의이 방법입니다.
'ReentrantLock'클래스는 재진입 잠금을 구현하고 'import java.util.concurrent.locks'패키지의 일부입니다. ReentrantLock 클래스는 공유 리소스에 액세스하기위한 메서드 동기화를 제공합니다. 클래스에는 스레드가 액세스 할 때 리소스를 잠금 / 잠금 해제하기위한 잠금 및 잠금 해제 메서드도 있습니다.
ReentrantLock의 독특한 기능 중 하나는 스레드가 ReentrantLock을 사용하여 공유 리소스를 두 번 이상 잠글 수 있다는 것입니다. 스레드가 리소스를 잠글 때 1로 설정되는 보류 횟수를 제공합니다.
스레드는 잠금을 해제하기 전에 리소스에 다시 들어가서 액세스 할 수 있습니다. 스레드가 재진입 잠금을 사용하여 리소스에 액세스 할 때마다 보류 횟수가 1 씩 증가합니다. 잠금 해제 할 때마다 보류 횟수가 1 씩 감소합니다.
보류 수가 0에 도달하면 공유 리소스의 잠금이 해제됩니다.
ReentrantLock 클래스는 잠금 생성자와 함께 전달할 수있는 부울 값인 공정성 매개 변수도 제공합니다. fairness 매개 변수가 true로 설정되면 한 스레드가 잠금을 해제 할 때마다 잠금이 가장 긴 대기 스레드로 전달됩니다. 이것은 기아를 예방합니다.
재진입 잠금은 다음과 같이 사용할 수 있습니다.
return_type method_name() { reentrantlock.lock(); try { //Do some work } catch(Exception e) { e.printStackTrace(); } finally { reentrantlock.unlock(); } }
ReentrantLock에 대한 잠금 해제 문은 항상 finally 블록에 있습니다. 이렇게하면 예외가 발생하더라도 잠금이 해제됩니다.
ReentrantLock을 이해하기 위해 Java 프로그램을 구현해 보겠습니다.
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을 사용하여 공유 리소스에 액세스 할 수 있습니다.
자바의 세마포어
스레드 동기화의 다음 방법은 Semaphore를 사용하는 것입니다. 세마포어라고하는이 구조를 사용하여 공유 리소스에 대한 액세스는 카운터를 통해 제어됩니다. 중요한 부분을 보호하고 누락 된 신호를 방지 할 수 있도록 스레드간에 신호가 전송됩니다.
세마포어는 이러한 프로세스를 동기화하여 동시 프로세스를 관리하는 데 사용되는 변수로 정의 할 수 있습니다. 세마포어는 공유 리소스에 대한 액세스를 동기화하여 경쟁 조건을 방지하는데도 사용됩니다. 세마포어에 의해 공유 리소스에 액세스하기 위해 스레드에 부여 된 권한을 허용이라고도합니다.
수행하는 기능에 따라 세마포어는 두 가지 유형으로 나눌 수 있습니다.
# 1) 이진 세마포어 : 이진 세마포어는 동시 프로세스를 동기화하고 상호 배제를 구현하는 데 사용됩니다. 이진 세마포어는 두 값, 즉 0과 1 만 가정합니다.
# 2) 세마포 계산 : 계수 세마포에는 임계 섹션에 들어갈 수있는 프로세스 수를 나타내는 값이 있습니다. 언제든지 값은 중요 섹션에 들어가는 최대 프로세스 수를 나타냅니다.
그렇다면 세마포어는 어떻게 작동합니까?
세마포어의 작동은 다음 단계로 요약 할 수 있습니다.
- 세마포어 개수가 0보다 크면 스레드가 임계 섹션에 액세스 할 수있는 권한이 있고 개수가 감소 함을 의미합니다.
- 그렇지 않으면 스레드는 허가를받을 때까지 차단됩니다.
- 스레드가 공유 리소스에 액세스하면 허용이 해제되고 세마포어 수가 증가하여 다른 스레드가 위 단계를 반복하여 허용을 얻을 수 있습니다.
세마포어 작동의 위 단계는 아래 순서도에 요약 될 수 있습니다.
자바에서는 세마포어를 구현할 필요는 없지만 신호기 세마포어 기능을 구현하는 클래스. Semaphore 클래스는 java.util.concurrent 꾸러미.
세마포어 클래스는 세마포어 객체를 생성 할 수있는 다음 생성자를 제공합니다.
Semaphore (int num_value) Semaphore (int num_value, boolean how)
여기,
num_value => 공유 리소스에 액세스 할 수있는 스레드 수를 결정하는 허용 횟수의 초기 값입니다.
어떻게 => 스레드에 허가가 부여되는 순서를 설정합니다 (방법 = true). how = false이면 그러한 순서를 따르지 않습니다.
이제 공유 리소스 액세스를 관리하고 경쟁 조건을 방지하는 데 사용되는 Semaphore를 보여주는 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. } }
산출
이 프로그램은 공유 리소스에 대한 클래스를 선언했습니다. 또한 클래스 생성자에서 초기화 된 세마포어 변수가있는 스레드 클래스를 선언합니다.
Thread 클래스의 재정의 된 run () 메서드에서는 스레드가 허가를 획득하고 공유 리소스에 액세스 한 다음 허가를 해제하는 스레드 인스턴스 처리가 수행됩니다.
메인 메서드에서 두 개의 스레드 인스턴스를 선언했습니다. 그런 다음 두 스레드가 모두 시작되고 결합 방법을 사용하여 대기합니다. 마지막으로 개수가 표시됩니다. 즉, 두 스레드가 공유 리소스로 완료되었음을 나타내는 0입니다.
Java에서 포크 및 조인
fork / join 프레임 워크는 Java 7에서 처음 도입되었습니다.이 프레임 워크는 병렬 처리 속도를 높일 수있는 도구로 구성됩니다. 시스템에서 사용 가능한 모든 프로세서 코어를 사용하고 작업을 완료합니다. 포크 / 조인 프레임 워크는 분할 및 정복 접근 방식을 사용합니다.
Fork / Join 프레임 워크의 기본 아이디어는 첫 번째 프레임 워크 '포크'입니다. 즉, 작업이 비동기 적으로 실행될 수 있도록 원 자성이 될 때까지 작업을 더 작은 개별 하위 작업으로 재귀 적으로 분할합니다.
이렇게하면 작업이 '조인'됩니다. 즉, 모든 하위 작업이 반복적으로 단일 작업 또는 반환 값으로 결합됩니다.
fork / join 프레임 워크에는 'ForkJoinPool'이라는 스레드 풀이 있습니다. 이 풀은 'ForkJoinWorkerThread'유형의 작업자 스레드를 관리하여 효과적인 병렬 처리를 제공합니다.
ForkJoinPool은 작업자 스레드를 관리하고 스레드 풀 성능 및 상태에 대한 정보를 얻을 수 있도록 도와줍니다. ForkJoinPool은 위에서 논의한 'ExecutorService'의 구현입니다.
작업자 스레드와 달리 ForkJoinPool은 각 하위 작업에 대해 별도의 스레드를 생성하지 않습니다. ForkJoinPool의 각 스레드는 작업을 저장하기 위해 deque (양방향 대기열)를 유지합니다.
데크는 스레드의 워크로드 밸런싱 역할을하며 아래에 설명 된 '작업 도용 알고리즘'의 도움으로이를 수행합니다.
워크 스틸 링 알고리즘
작업 도용 알고리즘을 다음과 같이 간단한 단어로 정의 할 수 있습니다. '스레드가 사용 가능한 경우 바쁜 스레드에서 작업을 '훔쳐''.
작업자 스레드는 항상 데크에서 작업을 가져옵니다. deque의 모든 작업이 소진되고 deque가 비어 있으면 작업자 스레드는 다른 deque의 꼬리 또는 'global entry queue'에서 작업을 가져옵니다.
이렇게하면 스레드가 작업을 위해 경쟁 할 가능성이 최소화되고 스레드가 작업을 위해 스카우트해야하는 횟수도 줄어 듭니다. 이는 스레드가 이미 사용 가능한 작업의 가장 큰 덩어리를 가져와 완료했기 때문입니다.
그렇다면 프로그램에서 ForkJoinPool을 어떻게 사용할 수 있습니까?
ForkJoinPool의 일반적인 정의는 다음과 같습니다.
public class ForkJoinPool extends AbstractExecutorService
ForkJoinPool 클래스는 'java.util.concurrent'패키지의 일부입니다.
Java 8에서는 공용 풀 또는 기본 스레드 풀에 대한 참조를 제공하는 정적 메서드 'common-pool ()'을 사용하여 ForkJoinPool의 인스턴스를 만듭니다.
ForkJoinPool commonPool = ForkJoinPool.commonPool ();
Java 7에서는 ForkJoinPool 인스턴스를 만들고 아래와 같이 유틸리티 클래스의 필드에 할당합니다.
public static ForkJoinPool forkJoinPool = new ForkJoinPool(2);
위의 정의는 풀이 2 개의 프로세서 코어를 사용하도록 풀의 병렬 처리 수준이 2임을 나타냅니다.
위의 풀에 액세스하기 위해 다음 진술을 제공 할 수 있습니다.
ForkJoinPool forkJoinPool = PoolUtil.forkJoinPool;
ForkJoinPool 작업의 기본 유형은 'ForkJoinTask'입니다. 하위 클래스 중 하나를 확장해야합니다. 즉, void 작업의 경우 RecursiveAction 및 값을 반환하는 작업의 경우 RecursiveTask. 두 확장 클래스 모두 작업의 논리를 정의하는 추상 메서드 계산 ()을 제공합니다.
아래에 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) Future Java는 무엇입니까?
대답: Future 객체 (java.util.concurrent.Future)는 Callable 인터페이스가 구현 될 때 스레드가 반환 한 결과를 저장하는 데 사용됩니다.
Q # 4) Java에서 스레드로부터 안전한 것은 무엇입니까?
대답: Java의 스레드 안전 코드 또는 클래스는 문제없이 다중 스레드 또는 동시 환경에서 공유 할 수 있고 예상되는 결과를 생성 할 수있는 코드 또는 클래스입니다.
Q # 5) Java에서 동기화 된 컬렉션이란 무엇입니까?
대답: 동기화 된 컬렉션은 스레드로부터 안전한 컬렉션입니다. java.util.Collections 클래스의 동기화 된 컬렉션 () 메서드는 동기화 된 (스레드 안전) 컬렉션을 반환합니다.
결론
이 튜토리얼을 통해 Java의 다중 스레딩 및 동시성 주제를 완료했습니다. 이전 튜토리얼에서 멀티 스레딩에 대해 자세히 논의했습니다. 여기서는 java.util.concurrent 패키지의 일부인 동시성 및 멀티 스레딩과 관련된 동시성 및 구현에 대해 설명했습니다.
두 가지 동기화 방법, 세마포어 및 ReentrantLock에 대해 논의했습니다. 또한 작업을 더 간단한 작업으로 나누고 결과를 결합하여 작업을 실행하는 데 사용되는 ForkJoinPool에 대해서도 논의했습니다.
java.util.concurrent 패키지는 스레드를 실행하는 데 도움이되는 Executor 프레임 워크와 실행기를 지원합니다. 또한 실행이 완료되면 풀로 반환되는 재사용 가능한 스레드로 구성된 스레드 풀 구현에 대해서도 논의했습니다.
우리는 Runnable과 유사한 또 다른 인터페이스에 대해 논의했습니다.이 인터페이스는 또한 얻은 스레드 결과를 저장하는 데 사용되는 스레드 및 Future 객체에서 결과를 반환하는 데 도움이됩니다.
추천 도서
- Thread.Sleep ()-Java의 스레드 Sleep () 메서드 예제 포함
- Java 배포 : Java JAR 파일 생성 및 실행
- Java 기초 : Java 구문, Java 클래스 및 핵심 Java 개념
- Java Virtual Machine : JVM이 Java 응용 프로그램을 실행하는 데 도움이되는 방법
- Java의 액세스 수정 자-예제가 포함 된 자습서
- Java Synchronized : Java에서 스레드 동기화 란?
- 초보자를위한 JAVA 튜토리얼 : 100 개 이상의 실습 Java 비디오 튜토리얼
- Java 정수 및 Java BigInteger 클래스 (예제 포함)