우리 모두는 JDK1.5 이전에 비즈니스 동시성이 Java로 구현되었을 때 일반적으로 프로그래머가 코드 구현을 독립적으로 완료해야 했다는 것을 알고 있습니다. 물론 이러한 기능을 제공하는 일부 오픈 소스 프레임워크가 있지만 여전히 유용하지는 않습니다. JDK와 함께 제공되는 기능이 편리합니다. 고품질의 자바 멀티스레드 동시 프로그램을 설계할 때, 자바를 사용하기 전 wait(), inform(), 동기화 등의 데드 점프 현상을 방지하기 위해 성능, 교착 상태, 공정성 등을 고려해야 하는 경우가 많다. 관리 및 스레드 안전으로 인한 피해를 방지하는 방법과 같은 많은 요소가 좀 더 복잡한 보안 전략을 채택하는 경우가 많으며 이로 인해 JDK1.5가 등장한 이후에는 Sun Master(Doug)가 개발 부담을 가중시킵니다. Lea)는 마침내 가난한 프로그래머를 위해 동시 완료를 단순화하기 위해 java.util.concurrent 툴킷을 도입했습니다. 개발자는 이를 사용하여 경쟁 조건과 교착 상태 스레드를 효과적으로 줄일 수 있습니다. 동시 패키지는 이러한 문제를 매우 잘 해결하고 보다 실용적인 동시 프로그램 모델을 제공합니다.
실행자: 특정 Runnable 작업의 실행자입니다.
ExecutorService: 스레드 풀 관리자로, 많은 구현 클래스가 있는데 그 중 일부를 소개하겠습니다. 예약을 위해 Runnable 및 Callable을 풀에 제출할 수 있습니다.
세마포어: 카운팅 세마포어
ReentrantLock: 동기화된 기능과 유사하지만 훨씬 더 강력한 재진입 상호 배타적 잠금입니다.
Future: 스레드 실행 후 반환된 결과를 가져오는 등 Runnable 및 Callable과 상호 작용하기 위한 인터페이스입니다. 또한 스레드를 종료하기 위한 취소 기능도 제공합니다.
BlockingQueue: 차단 대기열입니다.
CompletionService: 스레드 실행 결과를 얻을 수 있는 ExecutorService 확장
CountDownLatch: 하나 이상의 스레드가 다른 스레드에서 수행 중인 일련의 작업이 완료될 때까지 대기할 수 있도록 하는 동기화 도우미 클래스입니다.
CyclicBarrier: 스레드 그룹이 공통 장벽 지점에 도달할 때까지 서로 기다릴 수 있도록 하는 동기화 도우미 클래스
미래: 미래는 비동기 계산의 결과를 나타냅니다.
ScheduledExecutorService: 지정된 지연 후 또는 정기적인 간격으로 실행되도록 명령을 예약하는 ExecutorService입니다.
다음으로 하나씩 소개하겠습니다.
실행자 기본 메소드 설명
newFixedThreadPool(고정 크기 스레드 풀)
고정된 스레드 집합을 재사용할 수 있는 스레드 풀을 만들고 제한되지 않은 공유 대기열에서 이러한 스레드를 실행할 수 있습니다(요청된 스레드만 실행을 위해 대기열에서 대기합니다). 종료 전 실행 중 오류로 인해 스레드가 종료되면 새 스레드가 그 자리에서 후속 작업을 수행합니다(필요한 경우).
newCachedThreadPool(제한되지 않은 스레드 풀, 자동 스레드 재활용 수행 가능)
필요에 따라 새 스레드를 생성하지만 이전에 생성된 스레드가 사용 가능해지면 재사용하는 스레드 풀을 생성합니다. 많은 단기 비동기 작업을 수행하는 프로그램의 경우 이러한 스레드 풀은 종종 프로그램 성능을 향상시킵니다. 실행을 호출하면 이전에 생성된 스레드가 재사용됩니다(스레드가 사용 가능한 경우). 기존 스레드를 사용할 수 없으면 새 스레드가 생성되어 풀에 추가됩니다. 60초 동안 사용되지 않은 스레드를 종료하고 캐시에서 제거합니다. 따라서 오랫동안 유휴 상태로 있는 스레드 풀은 리소스를 사용하지 않습니다. ThreadPoolExecutor 생성자를 사용하면 속성은 비슷하지만 세부 정보(예: 시간 제한 매개 변수)가 다른 스레드 풀을 만들 수 있습니다.
newSingleThreadExecutor(단일 백그라운드 스레드)
단일 작업자 스레드를 사용하고 제한되지 않은 대기열에서 스레드를 실행하는 실행자를 만듭니다. (종료 전 실행 중 오류로 인해 이 단일 스레드가 종료되면 필요한 경우 새 스레드가 그 자리에서 후속 작업을 수행합니다.) 작업은 순차적으로 실행되도록 보장되며 주어진 시간에 하나 이상의 스레드가 활성화되지 않습니다. 동등한 newFixedThreadPool(1)과 달리 이 메서드에서 반환된 실행기는 재구성하지 않고도 다른 스레드를 사용할 수 있음이 보장됩니다.
이러한 메서드는 스레드 풀로 이해될 수 있는 ExecutorService 개체를 반환합니다.
이 스레드 풀의 기능은 비교적 완전합니다. submit()으로 작업을 제출하고 shutdown()으로 스레드 풀을 종료할 수 있습니다.
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public 클래스 MyExecutor 확장 스레드 {private int index;public MyExecutor(int i){ this.index=i;}public void run(){ try{ System.out.println("["+this.index+"] 시작...."); Thread.sleep((int)(Math.random()*)); System.out.println("["+this.index+"] end.") } catch(Exception e){ e.printStackTrace(); }}public static void main(String args[]){ ExecutorService service=Executors.newFixedThreadPool() for(int i=;i<;i++){ service.execute(new MyExecutor(i)); //service.submit(new MyExecutor(i)) } System.out.println("제출 완료");
일부 정보가 인쇄되어 있지만 이 스레드 풀이 어떻게 작동하는지 명확하지 않습니다. 수면 시간을 10배로 늘려보겠습니다.
Thread.sleep((int)(Math.random()*10000));
좀 더 자세히 살펴보면 4개의 스레드만 실행될 수 있다는 것을 분명히 알 수 있습니다. 스레드가 실행되면 새 스레드가 실행됩니다. 즉, 모든 스레드를 제출한 후 스레드 풀은 최종 종료가 실행될 때까지 기다립니다. 또한 제출 스레드가 "제한되지 않은 대기열"에 배치되어 있음을 알 수 있습니다. 이것은 순서가 지정된 대기열(BlockingQueue, 아래에서 설명함)입니다.
또한 Executor의 정적 함수를 사용하여 고정된 스레드 풀을 생성합니다. 이름에서 알 수 있듯이 스레드 풀의 스레드는 Idle 상태라도 해제되지 않습니다.
예를 들어 스레드 풀의 크기가 200인 경우 모든 스레드가 사용되면 모든 스레드는 계속해서 풀에 유지되고 해당 메모리 및 스레드 전환(while(true)+슬립 루프)이 발생합니다. )이 증가할 것이다.
이 문제를 피하려면 ThreadPoolExecutor()를 직접 사용하여 생성해야 합니다. 일반 스레드 풀과 마찬가지로 "최대 스레드 수", "최소 스레드 수", "유휴 스레드 keepAlive 시간"을 설정할 수 있습니다.
스레드 풀의 기본 사용법입니다.
신호기
카운팅 세마포어. 개념적으로 세마포어는 권한 모음을 유지 관리합니다. 필요한 경우 권한을 사용할 수 있을 때까지 각 acquire()가 차단된 다음 권한을 획득합니다. 각 release()는 권한을 추가하여 잠재적으로 차단 획득자를 해제합니다. 그러나 실제 라이센스 객체를 사용하는 대신 Semaphore는 사용 가능한 라이센스 수를 계산하고 그에 따라 조치를 취합니다.
세마포어는 특정 리소스(물리적 또는 논리적)에 액세스할 수 있는 스레드 수를 제한하는 데 자주 사용됩니다. 예를 들어 다음 클래스는 세마포어를 사용하여 콘텐츠 풀에 대한 액세스를 제어합니다.
실제 상황은 다음과 같습니다. 화장실에 가려면 모두가 줄을 서야 합니다. 화장실에 10명이 오면 줄을 서야 하는 자리가 두 군데뿐입니다.
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore;public 클래스 MySemaphore 확장 스레드 {Semaphore position;private int id;public MySemaphore(int i,Semaphore s){ this.id=i; this.position=s;}public void run(){ 시도{ if(position.availablePermits()>){ System.out.println("고객["+this.id+"]가 화장실에 들어갑니다. 공간이 있습니다.") } else{ System.out.println("Customer["+ this.id+"] 화장실에 들어갑니다. 공간이 없습니다."); } position.acquire(); System.out.println("고객 ["+this.id+"]가 피트 좌석을 얻습니다."); Thread.sleep((int)(Math.random()*)); System.out.println("고객 ["+this.id+"]가 사용을 완료했습니다."); position.release(); ) { e.printStackTrace(); }} public static void main(String args[]){ ExecutorService list=Executors.newCachedThreadPool(); Semaphore(); for(int i=;i<;i++){ list.submit(new MySemaphore(i+,position)) } list.shutdown(); position.acquireUninterruptously(); 완료되었습니다. 정리해야 합니다."); position.release();}}
재진입 잠금
동기화된 메소드 및 명령문을 사용하여 액세스되는 암시적 모니터 잠금과 동일한 기본 동작 및 의미 중 일부를 가지지만 더 강력한 재진입 뮤텍스 잠금입니다.
ReentrantLock은 가장 최근에 성공적으로 잠금을 획득했지만 아직 잠금을 해제하지 않은 스레드가 소유합니다. 다른 스레드가 잠금을 소유하지 않은 경우 잠금을 호출하는 스레드는 성공적으로 잠금을 획득하고 반환됩니다. 현재 스레드가 이미 잠금을 보유하고 있는 경우 이 메서드는 즉시 반환됩니다. isHeldByCurrentThread() 및 getHoldCount() 메서드를 사용하여 이러한 일이 발생하는지 확인할 수 있습니다.
이 클래스의 생성자는 선택적 공정성 매개변수를 허용합니다.
true로 설정하면 여러 스레드의 경합 하에 이러한 잠금이 가장 오래 기다린 스레드에 대한 액세스 권한을 부여하는 경향이 있습니다. 그렇지 않으면 이 잠금은 특정 액세스 순서를 보장하지 않습니다.
기본 설정(불공정 잠금 사용)과 비교할 때 공정 잠금을 사용하는 프로그램은 많은 스레드에서 액세스할 때 전체 처리량이 매우 낮지만(즉, 매우 느리거나 종종 극도로 느림) 잠금 획득 성능이 저하됩니다. 잠금 할당이 보장됩니다. 균형에 있어서는 차이가 작습니다.
그러나 공정한 잠금은 스레드 스케줄링의 공정성을 보장하지 않는다는 점에 유의해야 합니다. 따라서 공정한 잠금을 사용하는 많은 스레드 중 하나는 여러 가지 성공 가능성을 가질 수 있으며, 이는 다른 활성 스레드가 처리되지 않고 현재 잠금을 보유하고 있지 않을 때 발생합니다.
또한 시간이 지정되지 않은 tryLock 메서드는 공정성 설정을 사용하지 않습니다. 다른 스레드가 대기 중이더라도 잠금이 사용 가능한 한 이 방법은 성공할 수 있기 때문입니다.
항상 즉시 연습하고 try 블록을 사용하여 잠금을 호출하는 것이 좋습니다. 구성 전/후에서 가장 일반적인 코드는 다음과 같습니다.
class X { private final ReentrantLock lock = new ReentrantLock(); // ... public void m() { lock.lock(); // 조건이 유지될 때까지 차단 try { // ... 메소드 본문 } finally { 잠금. 잠금 해제() } }}
내 예:
가져오기 java.util.concurrent.ExecutorService; 가져오기 java.util.concurrent.Executors; 가져오기 java.util.concurrent.locks.ReentrantLock; 공개 클래스 MyReentrantLock 확장 Thread{TestReentrantLock 잠금; 개인 int id; 공개 MyReentrantLock(int i,TestReentrantLock 테스트) ){ this.id=i; this.lock=test;}공개 무효 실행(){ lock.print(id);}public static void main(String args[]){ ExecutorService service=Executors.newCachedThreadPool(); TestReentrantLock lock=new TestReentrantLock(); for(int i=;i<;i++){ 서비스. submit(new MyReentrantLock(i,lock)) } service.shutdown();}}class TestReentrantLock{private ReentrantLock 잠금=새 ReentrantLock(); public void print(int str){ try{ lock.lock(); System.out.println(str+"get") Thread.sleep((int)(Math.random()* )); } catch(Exception e){ e.printStackTrace(); } finally{ System.out.println(str+"release") }}}
차단 대기열
두 가지 추가 작업, 즉 요소를 검색할 때 대기열이 비어 있지 않을 때까지 기다리는 작업과 요소를 저장할 때 공간을 사용할 수 있을 때까지 기다리는 작업을 지원하는 대기열입니다.
BlockingQueue는 null 요소를 허용하지 않습니다. 일부 구현에서는 null 요소를 추가, 삽입 또는 제공하려고 할 때 NullPointerException이 발생합니다. null은 폴링 작업이 실패했음을 나타내는 경고 값으로 사용됩니다.
BlockingQueue는 용량이 제한될 수 있습니다. 주어진 시간에 남은 용량을 가질 수 있으며, 이를 초과하면 차단 없이 추가 요소를 넣을 수 없습니다.
내부 용량 제약이 없는 BlockingQueue는 항상 남은 용량의 Integer.MAX_VALUE를 보고합니다.
BlockingQueue 구현은 주로 생산자-소비자 대기열에 사용되지만 추가로 Collection 인터페이스도 지원합니다. 예를 들어, 제거(x)를 사용하여 대기열에서 임의의 요소를 제거하는 것이 가능합니다.
그러나 이 작업은 일반적으로 효율적으로 수행되지 않으며 메시지 대기열에서 빼는 경우와 같이 가끔 계획된 방식으로만 사용할 수 있습니다.
BlockingQueue 구현은 스레드로부터 안전합니다. 모든 대기열 방법은 내부 잠금이나 다른 형태의 동시성 제어를 사용하여 해당 목적을 자동으로 달성할 수 있습니다.
그러나 구현에서 특별히 명시하지 않는 한 많은 수의 컬렉션 작업(addAll, containAll, keepAll 및 RemoveAll)이 반드시 자동으로 수행되는 것은 아닙니다.
따라서 예를 들어 addAll(c)는 c에 일부 요소만 추가한 후에 실패(예외 발생)할 수 있습니다.
BlockingQueue는 기본적으로 더 이상 항목이 추가되지 않음을 나타내는 어떤 종류의 "닫기" 또는 "종료" 작업도 지원하지 않습니다.
이 기능의 필요성과 사용은 구현에 따라 달라지는 경향이 있습니다. 예를 들어, 일반적인 전략은 특별한 스트림 끝 또는 포이즌 개체를 생산자에게 삽입하고 소비자가 해당 개체를 얻는 시기에 따라 이를 해석하는 것입니다.
다음 예에서는 이 차단 대기열의 기본 기능을 보여줍니다.
import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;import java.util.concurrent.LinkedBlockingQueue;public 클래스 MyBlockingQueue 확장 Thread {public static BlockingQueue<String> 대기열 = 새로운 LinkedBlockingQueue<String>();개인 int 인덱스;공개 MyBlockingQueue(int i) { this.index = i;} public void run() { try { queue.put(String.valueOf(this.index)); System.out.println("{" + this.index + " } 대기열에 있습니다!"); } catch (예외 e) { e.printStackTrace(); }} public static void main(String args[]) { ExecutorService service = Executors.newCachedThreadPool(); for (int i = ; i < ; i++) { service.submit(new MyBlockingQueue(i)) } Thread thread = new Thread() { public void run() { try { while (true) { Thread.sleep((int) (Math.random() * )); if(MyBlockingQueue.queue.isEmpty()) String str = MyBlockingQueue.queue.take(); System.out.println(str + "has take!"); } } catch (Exception e) { e.printStackTrace() } } }; 일시 휴업();}}
---------------------------실행 결과----
{0}이(가) 대기열에 있습니다!
{1}이(가) 대기열에 있습니다!
{2}이(가) 대기열에 있습니다!
{3}이(가) 대기열에 있습니다!
0명이 찍었습니다!
{4}명이 대기열에 있습니다!
1이 걸렸습니다!
{6}명이 대기열에 있습니다!
2개 걸렸어요!
{7}이(가) 대기열에 있습니다!
3개가 걸렸어요!
{8}이(가) 대기열에 있습니다!
4개 걸렸어요!
{5}명이 대기열에 있습니다!
6개가 걸렸습니다!
{9}이(가) 대기열에 있습니다!
7이 걸렸습니다!
8이 걸렸습니다!
5개 걸렸어요!
9가 걸렸습니다!
---------------------------
완료서비스
새로운 비동기 작업 생성과 완료된 작업 결과 소비를 분리하는 서비스입니다. 생산자는 수행할 작업을 제출합니다. 사용자는 완료된 작업을 수행하고 완료된 순서대로 결과를 처리합니다. 예를 들어 CompletionService를 사용하면 읽기 작업을 수행하는 작업이 프로그램이나 시스템의 일부로 제출되고 읽기 작업이 완료되면 프로그램의 다른 부분에서 다른 작업이 수행됩니다. , 작업이 요청된 순서가 다를 수 있습니다.
일반적으로 CompletionService는 실제로 작업을 수행하기 위해 별도의 Executor에 의존하며, 이 경우 CompletionService는 내부 완료 큐만 관리합니다. ExecutorCompletionService 클래스는 이 메서드의 구현을 제공합니다.
import java.util.concurrent.Callable;import java.util.concurrent.CompletionService;import java.util.concurrent.ExecutorCompletionService;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public 클래스 MyCompletionService는 Callable을 구현합니다. <String> {private int id;public MyCompletionService(int i){ this.id=i;}public static void main(String[] args) throw Exception{ ExecutorService service=Executors.newCachedThreadPool(); CompletionService<String> 완성=new ExecutorCompletionService<String>(service); i<;i++){ 완성.submit(new MyCompletionService(i)) } for(int i=;i<;i++){ System.out.println(completion.take().get()); } service.shutdown();} public String call() throws Exception { Integer time=(int)(Math.random()*); System.out.println(this.id+" start"); Thread.sleep(time); System.out.println(this.id+" end"); e.printStackTrace(); } return this.id+":"+time;}}
카운트다운래치
하나 이상의 스레드가 다른 스레드에서 수행 중인 일련의 작업이 완료될 때까지 대기할 수 있도록 하는 동기화 도우미 클래스입니다.
주어진 개수로 CountDownLatch를 초기화합니다. countDown() 메서드가 호출되기 때문에 현재 개수가 0에 도달할 때까지 wait 메서드가 차단됩니다.
그 후에는 대기 중인 모든 스레드가 해제되고 대기하는 모든 후속 호출이 즉시 반환됩니다. 이 동작은 한 번만 발생하며 카운트를 재설정할 수 없습니다. 카운트를 재설정해야 하는 경우 CyclicBarrier 사용을 고려하세요.
CountDownLatch는 다양한 용도로 사용되는 일반 동기화 도구입니다. 카운트 1로 초기화된 CountDownLatch를 간단한 켜기/끄기 래치 또는 항목으로 사용합니다. wait를 호출하는 모든 스레드는 countDown()을 호출하는 스레드에 의해 항목이 열릴 때까지 해당 항목에서 기다립니다.
N으로 초기화된 CountDownLatch는 N 스레드가 작업을 완료할 때까지 스레드를 기다리거나 작업이 N번 완료될 때까지 기다리게 할 수 있습니다.
CountDownLatch의 유용한 기능은 계속하기 전에 countDown 메서드를 호출하는 스레드가 카운트가 0에 도달할 때까지 기다릴 필요가 없고 오히려 모든 스레드가 통과할 수 있을 때까지 대기를 통해 스레드가 계속되는 것을 방지한다는 것입니다.
아래 예는 다른 사람이 작성한 것으로 매우 생생합니다.
import java.util.concurrent.CountDownLatch;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class TestCountDownLatch {public static void main(String[] args) throws InterruptedException { // 시작 카운트다운 잠금 최종 CountDownLatch start = new CountDownLatch(); // 카운트다운 잠금 끝 final CountDownLatch end = new CountDownLatch(); // 10명의 참가자 final Executors.newFixedThreadPool(); for (int index = ; index < ; index++) { final int NO = index + ; public void run( ) { try { start.await();//항상 Thread.sleep((long) (Math.random() * ))을 차단함; System.out.println("No." + NO + "arrived"); } catch (InterruptedException e) { } finally { end.countDown() } } } System.out. println("게임 시작"); end.await(); System.out.println("게임 종료");
CountDownLatch의 가장 중요한 메소드는 countDown()과 wait()입니다. 전자는 주로 한 번 카운트다운하고, 후자는 0이 될 때까지 기다립니다. 0에 도달하지 않으면 차단하고 대기만 합니다.
순환 장벽
스레드 그룹이 공통 장벽 지점에 도달할 때까지 서로 기다릴 수 있도록 하는 동기화 도우미 클래스입니다.
CyclicBarrier는 때때로 서로를 기다려야 하는 고정 크기 스레드 집합이 포함된 프로그램에 유용합니다. 대기 중인 스레드가 해제된 후에 장벽을 재사용할 수 있으므로 순환 장벽이라고 합니다.
CyclicBarrier는 스레드 집합의 마지막 스레드가 도착한 후(그러나 모든 스레드가 해제되기 전) 각 장벽 지점에서 한 번만 실행되는 선택적 Runnable 명령을 지원합니다. 이 장벽 작업은 참여하는 모든 스레드를 계속하기 전에 공유 상태를 업데이트할 때 유용합니다.
사용 예: 다음은 매우 고전적인 투어 그룹 예인 병렬 분해 설계에서 장벽을 사용하는 예입니다.
import java.text.SimpleDateFormat;import java.util.Date;import java.util.concurrent.BrokenBarrierException;import java.util.concurrent.CyclicBarrier;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors; public class TestCyclicBarrier { // 산행 소요시간 : 심천, 광저우, 소관, 창사, 무한 private static int[] timeWalk = { , , , , , }; // 자율주행 투어 private static int[] timeSelf = { , , , , }; // 관광버스 private static int[] timeBus = { , , , , } ; static String now() { SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); sdf.format(new Date()) + ": "; } 정적 클래스 Tour는 Runnable을 구현합니다. this.times = 회; this.tourName = TourName; this.barrier = 장벽; } public void run() { try { Thread.sleep(times[] * ); System.out.println(now() + TourName + " 심천에 도달함"); ); Barrier.await(); Thread.sleep(times[] * ); System.out.println(now() +tourName + "샤오관에 도달함"); Barrier.await(); Thread.sleep(times[] * ); System.out.println(now() + TourName + " 장샤에 도달함") Thread.sleep(times[] * ); System.out.println(now() + TourName + " 우한에 도달함"); Barrier.await() } catch (InterruptedException e) { } catch (BrokenBarrierException e) { } } } public static void main(String[] args) { // 세 개의 투어 그룹 CyclicBarrier Barrier = new CyclicBarrier() ExecutorService exec = Executors.newFixedThreadPool(); "WalkTour", timeWalk)) exec.submit(new Tour(장벽, "SelfTour", timeSelf));//다음 코드를 주석 처리하면 프로그램이 차단되어 계속 실행할 수 없음을 알 수 있습니다. exec.submit(new Tour(barrier, "BusTour", timeBus)); exec.shutdown() }}
CyclicBarrier의 가장 중요한 속성은 참여자 수이며, 가장 중요한 메소드는 wait()이다. 모든 스레드가 wait()를 호출하면 해당 스레드가 계속 실행될 수 있고 그렇지 않으면 대기한다는 의미입니다.
미래
Future는 비동기 계산의 결과를 나타냅니다. 계산이 완료되었는지 확인하고, 계산이 완료될 때까지 기다리고, 계산 결과를 가져오는 메소드를 제공합니다.
계산이 완료된 후에는 get 메서드만 사용하여 결과를 검색할 수 있습니다. 필요한 경우 계산이 완료되기 전에 이 메서드를 차단할 수 있습니다. 취소는 cancel 메소드로 수행됩니다.
작업이 정상적으로 완료되었는지 또는 취소되었는지 확인하기 위한 추가 메서드가 제공됩니다. 계산이 완료되면 취소할 수 없습니다.
취소 가능성을 위해 Future를 사용하지만 사용 가능한 결과를 제공하지 않는 경우 Future<?> 형식 유형을 선언하고 기본 작업의 결과로 null을 반환할 수 있습니다.
이전에 CompletionService에서 이 Future의 함수를 보았으며 이는 스레드를 제출할 때 반환 객체로 지정될 수 있습니다.
ScheduledExecutorService
지정된 지연 후 또는 정기적인 간격으로 명령이 실행되도록 예약하는 ExecutorService입니다.
Schedule 메소드는 다양한 지연이 있는 작업을 생성하고 실행을 취소하거나 확인하는 데 사용할 수 있는 작업 개체를 반환합니다. ScheduleAtFixedRate 및 ScheduleWithFixedDelay 메소드는 취소될 때까지 주기적으로 실행되는 특정 작업을 생성하고 실행합니다.
Executor.execute(java.lang.Runnable) 및 ExecutorService의 제출 메소드를 사용하여 제출된 명령은 요청 지연 0으로 예약됩니다.
일정 메서드에서는 0 및 음수 지연(기간 제외)이 허용되며 이는 즉시 실행되는 요청으로 처리됩니다.
모든 일정 방법은 절대적인 시간이나 날짜가 아닌 상대적인 지연과 기간을 매개변수로 받아들입니다. Date로 표현되는 절대시간을 원하는 형태로 변환하는 것은 쉽습니다.
예를 들어, 나중에 실행되도록 예약하려면 다음을 사용하십시오: Schedule(task, date.getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS).
그러나 네트워크 시간 동기화 프로토콜, 시계 드리프트 또는 기타 요인으로 인해 상대적으로 지연된 만료 날짜는 활성화된 작업의 현재 날짜와 일치할 필요가 없습니다.
Executors 클래스는 이 패키지에 제공된 ScheduledExecutorService 구현을 위한 편리한 팩토리 메서드를 제공합니다.
다음 예는 인터넷에서도 인기가 있습니다.
import static java.util.concurrent.TimeUnit.SECONDS;import java.util.Date;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.ScheduledFuture;public 클래스 TestScheduledThread { 공개 정적 무효 메인(String[] args) { 최종 ScheduledExecutorService 스케줄러 = Executors.newScheduledThreadPool(); final Runnable beeper = new Runnable() { int count = ; public void run() { System.out.println(new Date() + " beep " + (++count)); // 몇 초 후에 실행하고 매 초마다 실행 final ScheduledFuture beeperHandle = Scheduler.scheduleAtFixedRate(beeper, , , SECONDS); // 몇 초 후에 실행하고 마지막 작업 실행이 완료된 후 몇 초 동안 기다렸다가 매번 다시 실행합니다. final ScheduledFuture beeperHandle = Scheduler.scheduleWithFixedDelay(beeper, , , SECONDS) // 몇 초 후에 작업을 종료하고 스케줄러 스케줄러를 닫습니다. .schedule(new Runnable() { public void run() { beeperHandle.cancel(true); beeperHandle.cancel(true); Scheduler.shutdown(); } }, , SECONDS);}}
이런 식으로 동시 패키지의 더 중요한 기능을 요약했습니다. 이해에 도움이 되기를 바랍니다.