2. 소개
멀티 스레딩 기술은 주로 프로세서 장치에서 여러 스레드 실행 문제를 해결합니다. 프로세서 장치의 유휴 시간을 크게 줄이고 프로세서 장치의 처리량 기능을 증가시킬 수 있습니다. 그러나 빈번한 스레드 생성의 오버 헤드는 매우 큽니다. 따라서 오버 헤드 의이 부분을 줄이려면 스레드 풀 사용을 고려해야합니다. 스레드 풀은 스레드 컨테이너로 한 번에 정격 스레드 수만 실행합니다. 스레드 풀은 이러한 정격 스레드 수를 관리하는 데 사용됩니다.
3. 스레드 풀과 관련된 클래스 구조 다이어그램
그중에서도 우리가 사용할 주된 것은 ThreadPooleExecutor 클래스입니다.
4. 스레드 풀을 만드는 방법
우리는 일반적으로 스레드 풀을 만들기위한 다음과 같은 방법이 있습니다.
1. Executors Factory Class를 사용하십시오
실행자는 주로 스레드 풀을 만드는 다음 방법을 제공합니다.
아래의 사용 예를 살펴 보겠습니다.
1) NewFixedThreadpool (고정 스레드 풀)
public class fixedthreadpool {public static void main (string [] args) {executorService pool = executors.newfixedThreadpool (5); // 고정 된 크기가 5 인 스레드 풀을 만듭니다 (int i = 0; i <10; i ++) {pool.submit (new mythread ()); } pool.shutdown (); }} public class mythread는 스레드를 확장합니다. }}테스트 결과는 다음과 같습니다.
풀 -1- 스레드 -1이 실행 중입니다. . .
풀 -1- 스레드 -2가 실행 중입니다. . .
Pool-1-Shread-3이 실행 중입니다. . .
풀 -1- 스레드 -2가 실행 중입니다. . .
Pool-1-Shread-3이 실행 중입니다. . .
풀 -1- 스레드 -2가 실행 중입니다. . .
풀 -1- 스레드 -2가 실행 중입니다. . .
Pool-1-Shread-3이 실행 중입니다. . .
풀 -1- 스레드 -5가 실행 중입니다. . .
Pool-1-Shread-4가 실행 중입니다. . .
고정 크기 스레드 풀 : 스레드가 스레드 풀의 최대 크기에 도달 할 때까지 작업을 제출할 때마다 스레드를 만듭니다. 스레드 풀의 크기는 최대 값에 도달하면 변경되지 않습니다. 실행 예외로 인해 스레드가 종료되면 스레드 풀이 새 스레드를 추가합니다.
2) NewsingLethreadExecutor (단일 스레드 풀)
공개 클래스 SingleThreadpool {public static void main (String [] args) {ExecutorService Pool = Executors.newsingLethreadExecutor (); // (int i = 0; i <100; i ++) {pool.submit (new Mythread ()); } pool.shutdown (); }}테스트 결과는 다음과 같습니다.
풀 -1- 스레드 -1이 실행 중입니다. . .
풀 -1- 스레드 -1이 실행 중입니다. . .
풀 -1- 스레드 -1이 실행 중입니다. . .
풀 -1- 스레드 -1이 실행 중입니다. . .
풀 -1- 스레드 -1이 실행 중입니다. . .
풀 -1- 스레드 -1이 실행 중입니다. . .
풀 -1- 스레드 -1이 실행 중입니다. . .
풀 -1- 스레드 -1이 실행 중입니다. . .
풀 -1- 스레드 -1이 실행 중입니다. . .
풀 -1- 스레드 -1이 실행 중입니다. . .
단일 스레드 스레드 풀 :이 스레드 풀에는 하나의 스레드가 작동합니다. 즉, 모든 작업의 단일 스레드 직렬 실행을 의미합니다. 이 고유 한 스레드가 예외로 인해 종료되면 교체 할 새로운 스레드가 있습니다. 이 스레드 풀은 모든 작업의 실행 순서가 작업 제출 순서대로 실행되도록합니다.
3) NewsCheduledthreadpool
public class scheduledthreadpool {public static void main (String [] args) {scheduledExecutorService Pool = Executors.newScheduledThreadPool (6); for (int i = 0; i <10000; i ++) {pool.submit (new Mythread ()); } pool.schedule (new Mythread (), 1000, TimeUnit.milliseconds); pool.schedule (new Mythread (), 1000, TimeUnit.milliseconds); pool.shutdown (); }}테스트 결과는 다음과 같습니다.
풀 -1- 스레드 -1이 실행 중입니다. . .
Pool-1-Shread-6이 실행 중입니다. . .
풀 -1- 스레드 -5가 실행 중입니다. . .
Pool-1-Shread-4가 실행 중입니다. . .
풀 -1- 스레드 -2가 실행 중입니다. . .
Pool-1-Shread-3이 실행 중입니다. . .
Pool-1-Shread-4가 실행 중입니다. . .
풀 -1- 스레드 -5가 실행 중입니다. . .
Pool-1-Shread-6이 실행 중입니다. . .
풀 -1- 스레드 -1이 실행 중입니다. . .
………………………………………………………………………………………………………………………………………………………………… …………………………………………………………………………………………………………………………………………………………………
Pool-1-Shread-4가 실행 중입니다. . .
풀 -1- 스레드 -1이 실행 중입니다. . .
테스트 결과의 마지막 두 스레드는 1s를 지연시킨 후에 만 실행하기 시작합니다. 이 스레드 풀은 타이밍 및 주기적 작업 실행 요구 사항을 지원합니다.
4) NewCachedthreadpool (캐시 가능한 스레드 풀)
public class cachedthreadpool {public static void main (string [] args) {executorService pool = executors.newCachedThreadPool (); for (int i = 0; i <100; i ++) {pool.submit (new Mythread ()); } pool.shutdown (); }}테스트 결과는 다음과 같습니다.
풀 -1- 스레드 -5가 실행 중입니다. . .
풀 -1- 스레드 -7이 실행 중입니다. . .
풀 -1- 스레드 -5가 실행 중입니다. . .
풀 -1- 스레드 -16이 실행 중입니다. . .
풀 -1- 스레드 -17이 실행 중입니다. . .
풀 -1- 스레드 -16이 실행 중입니다. . .
풀 -1- 스레드 -5가 실행 중입니다. . .
풀 -1- 스레드 -7이 실행 중입니다. . .
풀 -1- 스레드 -16이 실행 중입니다. . .
풀 -1- 스레드 -18이 실행 중입니다. . .
풀 -1- 스레드 -10이 실행 중입니다. . .
캐시 가능한 스레드 풀 : 스레드 풀의 크기가 작업을 처리하는 데 필요한 스레드를 초과하면 일부 유휴 스레드 (60 초 안에 작업 실행 없음)가 재활용됩니다. 작업 횟수가 증가하면이 스레드 풀은 작업을 처리하기 위해 새 스레드를 지능적으로 추가 할 수 있습니다. 이 스레드 풀은 스레드 풀 크기를 제한하지 않으며, 이는 운영 체제 (또는 JVM)가 생성 할 수있는 최대 스레드 크기에 전적으로 의존합니다.
공무원은 프로그래머가보다 편리한 집행자 공장 방법 집행자를 사용한다고 제안합니다. NewCachedThreadpool () (자동 스레드 재활용을 수행 할 수있는 무한 스레드 풀), Executor.NewFixedThreadPool (INT) (고정 크기 스레드 풀) Executor.NewsingLethreadExecutor (단일 배경 스레드). 이 스레드 풀은 대부분의 사용 시나리오에 대한 기본 구성으로 사전 정의됩니다.
2. ThreadPooleExecutor 클래스를 상속하고 부모 클래스의 생성자 메소드를 복사하십시오.
이 방법을 소개하기 전에 스레드 풀 생성을위한 이전 몇 가지 기본 코드를 분석하겠습니다.
공개 클래스 집행자 {public static executorService newFixedThreadpool (int nthreads) {return new Threads, Nthreads, 0L, TimeUnit.milliseconds, New LinkedBlockingQueue <Runnable> ()); } public static executorService NewsingLethreadExecutor () {return new finalizabledEgatedExecutorService (new ThreadPooleExecutor (1, 1, 0L, timeUnit.milliseconds, new LinkedBlockingQueue <Runnable> ()); }}Executors Factory Class의 기본 코드에서 공장 클래스가 스레드 풀 생성을 위해 제공 한 방법이 실제로 ThreadPooleExecutor를 구성하여 구현임을 알 수 있습니다. ThreadPooleExecutor 생성자 메소드 코드는 다음과 같습니다.
Public ThreadPooleExecutor (int corepoolsize, int maximumpoolsize, leg recoyalivetime, timeUnit itor, blockingqueue <runnable> workqueue, strandfactory strandfactory, 거부 거부 executionHandler handler) {if (corepoolize <0 || maxImumpoolsize <0 | 불법 행위 덱싱 (); if (workqueue == null || threadfactory == null || handler == null) 던지기 nullpointerexception (); this.corepoolsize = corepoolsize; this.maximumpoolsize = maximumpoolsize; this.workqueue = Workqueue; this.keepalivetime = init.tonanos (recopalivetime); this.threadFactory = ThreadFactory; this.handler = handler; }그런 다음 ThreadPooleExecutor 생성자 메소드에 대해 이야기 해 봅시다. 이 구성 방법에는 주로 다음 매개 변수가 있습니다.
CorePoolSize- 무료 스레드를 포함하여 수영장에 저장된 스레드 수.
MaxImumpOlsize - 수영장에서 허용되는 최대 스레드 수입니다.
KeepAliveTime-스레드 수가 CorePoolSize보다 큰 경우 유휴 스레드가 새로운 작업을 기다리는 데 가장 긴 시간입니다.
단위- Keepalivetime 매개 변수 시간 단위.
Workqueue- 실행 전에 작업을 유지하는 데 사용되는 대기열. 이 큐는 실행 메소드에서 제출 한 실행 가능한 작업 만 유지합니다.
ThreadFactory- 새로운 스레드를 만들기 위해 집행자가 사용하는 공장.
핸들러-실행에 사용되는 핸들러는 스레드 범위 및 큐 용량으로 인해 차단됩니다.
다음 으로이 매개 변수 간의 관계에 대해 이야기 해 봅시다. 스레드 풀이 방금 생성되면 스레드 풀에 스레드가 없습니다 (스레드 풀이 생성되는 즉시 특정 수의 스레드가 생성되지는 않습니다). execute () 메소드가 호출되면 작업을 추가하면 스레드 풀이 다음과 같은 판단을 내립니다.
1) 현재 실행중인 스레드 수가 CorePoolSize보다 적은 경우 즉시 새 스레드를 작성 하여이 작업을 수행하십시오.
2) 현재 실행중인 스레드 수가 CorePoolSize보다 크거나 동일하면이 작업은 대기열에 넣습니다.
3) 스레드 풀 큐가 가득 차 있지만 실행중인 스레드 수가 MaximumpOollsize보다 작 으면이 작업을 수행하기 위해 새 스레드가 여전히 생성됩니다.
4) 대기열이 가득 차 있고 현재 실행중인 스레드의 수가 MaximumpOollsize보다 크거나 동일하면 스레드 풀은 거부 정책에 따라 현재 작업을 처리합니다.
5) 작업이 실행되면 스레드는 큐에서 다음 작업을 수행하여 실행합니다. 대기열에서 실행해야 할 작업이 없으면 스레드가 유휴 상태입니다. KeepaliveTime의 생존 시간이 초과되면 스레드가 스레드 풀에 의해 재활용됩니다 (참고 : 재활용 스레드는 조건부입니다. 현재 실행중인 스레드의 수가 CorePoolSize보다 크면 스레드가 파괴됩니다. CorePoolSize보다 크지 않으면 스레드 수는 Corepoolsize의 수 내에 유지되어야합니다. 왜 실이 유휴 상태가 되 자마자 재활용되지는 않지만 스레드가 재활용되기 전에 keepalivetime을 초과 할 때까지 기다려야합니까? 그 이유는 매우 간단합니다. 실의 창조와 파괴는 많이 소비하고 자주 생성되고 파괴 될 수 없기 때문입니다. Keepalivetime을 초과 한 후에는이 실이 실제로 사용되지 않으며 파괴 될 것입니다. 이 경우, 단위는 requalivetime의 시간 단위를 나타내고, 단위의 정의는 다음과 같습니다.
public enum timeUnit {nanoseconds {// nanoseconds}, microseconds {// microseconds}, microseconds}, milliseconds {// milliseconds}, sec 시간}, days {// days in days}; 아래 소스 코드를 분석하겠습니다. 위의 상황에서 주로 관련된 소스 코드는 다음과 같습니다.
개인 부울 AddifunderCorePoolSize (runnable FirstTask) {Thread T = NULL; 최종 재진입 락 Mainlock = this.mainlock; mainlock.lock (); try {if (poolsize <corepoolsize && runstate == running) t = addThread (FirstTask); } 마침내 {mainLock.unlock (); } if (t == null) false를 반환합니다. t.start (); 진실을 반환하십시오. } 실제로이 코드는 매우 간단합니다. 주로 현재 스레드 풀이 CorePoolSize보다 작 으면 작업을 처리하기 위해 새 스레드가 작성되었다고 설명합니다.
개인 부울 addifunderMaxImumpOlsize (runnable FirstTask) {스레드 t = null; 최종 재진입 락 Mainlock = this.mainlock; mainlock.lock (); try {if (poolsize <maximumpoolsize && runstate == running) t = addThread (FirstTask); } 마침내 {mainLock.unlock (); } if (t == null) false를 반환합니다. t.start (); 진실을 반환하십시오. }위의 코드는 현재 스레드 풀의 수가 maximumpoolsize보다 적은 경우 작업을 실행하기 위해 스레드가 생성 될 것이라고 설명합니다.
5. 스레드 풀의 대기열
스레드 풀 큐의 3 가지 유형이 있습니다.
직접 커밋 : 작업 대기열의 기본 옵션은 동기식이며 작업을 보관하지 않고 스레드에 직접 제출합니다. 여기서 작업을 즉시 실행할 수있는 스레드가 없으면 작업을 대기하려고 시도하면 실패하여 새 스레드가 구성됩니다. 이 정책은 내부 종속성이있을 수있는 요청 세트를 처리 할 때 잠금을 피합니다. 직접 제출물은 일반적으로 새로 제출 된 작업을 거부하지 않기 위해 무한한 MaxImumpOlsize가 필요합니다. 이 전략을 통해 무한한 스레드는 큐가 처리 할 수있는 평균으로 명령이 지속적으로 도착하면 성장 가능성을 가질 수 있습니다.
무한한 큐 : 무한 큐를 사용하면 (예 : 사전 정의 된 용량이없는 LinkedBlockingqueue) 모든 CorePoolSize 스레드가 바쁠 때 새로운 작업이 대기열에서 대기 할 수 있습니다. 이러한 방식으로 생성 된 스레드는 CorePoolSize를 초과하지 않습니다. (maximumpoolsize의 값은 유효하지 않습니다.) 각 작업이 다른 작업과 완전히 독립적 일 때, 즉 작업 실행이 서로 영향을 미치지 않으면, 무한 큐에 적합합니다. 예를 들어 웹 페이지 서버에서 이 대기열은 과도 버스트 요청을 처리하는 데 사용될 수 있으며,이 전략을 사용하면 언급되지 않은 스레드가 큐가 처리 할 수있는 평균을 지속적으로 초과 할 때 생존되지 않은 스레드가 성장 가능성을 가질 수 있습니다.
제한된 대기열 : 제한된 MaxImumpOollsize를 사용할 때 경계 대기열 (ArrayBlockingqueue 등)은 자원 피로를 방지하는 데 도움이되지만 조정 및 제어하기가 어려울 수 있습니다. 대기열 크기와 최대 풀 크기는 서로 거래해야 할 수도 있습니다. 대형 대기열과 작은 풀을 사용하면 CPU 사용량, 운영 체제 리소스 및 컨텍스트 전환 오버 헤드를 최소화 할 수 있지만 수동 처리량을 줄일 수 있습니다. 작업이 자주 차단되면 (예 : I/O 경계 인 경우) 시스템은 허용하는 것보다 더 많은 스레드에 대한 시간을 예약 할 수 있습니다. 작은 대기열을 사용하려면 일반적으로 풀 크기가 더 크고 CPU 사용량이 높지만 용납 할 수없는 스케줄링 오버 헤드가 발생하여 처리량을 줄일 수 있습니다.
아래 스레드 풀 큐에 대해 이야기 해 봅시다. 클래스 구조 다이어그램은 다음과 같습니다.
1) 동기식
큐는 위에서 언급 한 직접 제출에 해당합니다. 우선, Synchronousqueue는 무한대가 없으므로 숫자를 저장하는 능력이 무제한이라는 것을 의미합니다. 그러나 큐 자체의 특성으로 인해 요소를 추가 한 후에는 다른 스레드가 계속 추가되기 전에 다른 스레드를 제거 할 때까지 기다려야합니다.
2) LinkedBlockingqueue
큐는 위의 무한 큐에 해당합니다.
3) ArrayBlockingqueue
큐는 위의 경계 큐에 해당합니다. ArrayBlockingqueue에는 다음과 같은 3 개의 생성자가 있습니다.
public arrayblockingqueue (int capacity) {this (용량, false); } public arrayblockingqueue (int 용량, 부울 박람회) {if (용량 <= 0) 새로운 불법 불법 행위 렉싱 (); this.items = (e []) 새 개체 [용량]; 잠금 = 새로운 재진입 락 (FAIR); notempty = lock.newcondition (); notfull = lock.newcondition (); } public arrayblockingqueue (int 용량, 부울 페어, 컬렉션 <? extends e> c) {this (용량, 공정); if (용량 <c.size ()) Throw New ImpalargumentException (); for (iterator <? extends e> it = c.ertator (); it.hasnext ();) add (it.next ()); }이 박람회에 집중합시다. Fair는 큐 액세스 스레드의 경쟁 전략을 나타냅니다. TRUE 일 때 작업 삽입 대기열은 FIFO 규칙을 준수합니다. False 인 경우 "대기열을자를 수 있습니다". 예를 들어, 지금 대기하는 많은 작업이 있으면 스레드가 작업을 완료하고 새로운 작업이 발생합니다. 거짓 인 경우이 작업을 대기열에서 대기시킬 필요는 없습니다. 대기열을 직접 자른 다음 실행할 수 있습니다. 아래 그림과 같이 :
6. 스레드 풀 거부 실행 전략
스레드의 수가 최대 값에 도달하면 현재 작업이 여전히 나오고 있으며 현재 작업을 거부해야합니다.
ThreadPooleExecutor는 작업을 추가 할 때 실행 정책을 사용자 정의 할 수 있습니다. 스레드 풀의 setReceDexecutionHandler () 메소드를 호출하고 기존 정책을 사용자 정의 된 거부 executionHandler 개체로 바꿀 수 있습니다. ThreadPooleExecutor가 제공하는 기본 처리 전략은 동시에 예외 정보를 직접 버리고 던지는 것입니다. ThreadPooleExecutor는 4 개의 기존 정책, 즉 다음을 제공합니다.
ThreadPoolexecutor.abortPolicy : 작업이 거부되고 예외가 발생했음을 나타냅니다. 소스 코드는 다음과 같습니다.
공개 정적 클래스 AbortPolicy는 거부 executionHandler { /***를 구현합니다. * / public abortpolicy () {} / *** 항상 거부 executionException을 던집니다. * @param r 실행 가능한 작업을 수행하도록 요청한 * @param e이 작업을 실행하려고 시도하는 집행자가 항상 거부했습니다. */ public void RejectedExecution (runnable r, ThreadPooleExecutor e) {throw new defecedExecutionException (); // 예외 던지기}}ThreadPoolexecutor.DiscardPolicy : 작업이 거부되었지만 조치가 수행되지 않았 음을 의미합니다. 소스 코드는 다음과 같습니다.
공개 정적 클래스 DiscardPolicy는 RejectedExecutionHandler { /***를 구현합니다. * / public discardpolicy () {} / *** 아무것도하지 않으며, 이는 폐기 작업 r입니다. * @param r 실행 요청 된 실행 요청 * @param e이 작업을 실행하려는 집행자가 */ public void develecedExecution (runnable r, threadpoolexecutor e) {} // 직접 거부하지만 아무것도하지 않지만}ThreadPoolExecutor.callerRunspolicy : 작업이 거부되고 발신자의 스레드에서 작업이 직접 실행되었음을 나타냅니다. 소스 코드는 다음과 같습니다.
공개 정적 클래스 CallerRunspolicy는 거부 excutionHandler { /***를 구현합니다. * / public callerrunspolicy () {} / ** * executor *가 종료되지 않는 한 발신자 스레드에서 작업 r을 실행합니다.이 경우 작업이 폐기됩니다. * @param r 실행 가능한 작업 * @param e이 작업을 실행하려는 집행자가 */ public void RejectedExecution (runnable r, threadpoolexecutor e) {if (! e.isshutdown ()) {r.run (); // 작업을 직접 실행}}}ThreadPoolExecutor.discardOldestPolicy : 작업 대기열의 첫 번째 작업이 먼저 폐기 된 다음 작업이 큐에 추가되었음을 의미합니다. 소스 코드는 다음과 같습니다.
공개 정적 클래스 DiscardEldestPolicy는 거부 executionHandler { /***가 주어진 집행자에게 <tt> discardOldEStPolicy < /tt>를 만듭니다. */ public discraderDestPolicy () {} public void RejectedExecution (runnable r, threadpoolexecutor e) {if (! e.isshutdown ()) {e.getqueue (). poll (); // 대기열에서 첫 번째 작업을 폐기합니다. E.Execute (r); // 새 작업 실행}}}작업이 지속적으로 도착하면 대기열에서 작업이 폴링되고 새로운 작업이 실행됩니다.
요약
위는 편집자가 소개 한 JDK 자체 스레드 풀에 대한 자세한 설명입니다. 모든 사람에게 도움이되기를 바랍니다. 궁금한 점이 있으면 메시지를 남겨 주시면 편집자가 제 시간에 모든 사람에게 답장을 드리겠습니다. Wulin.com 웹 사이트를 지원해 주셔서 대단히 감사합니다!