Wait () 및 notify ()를 사용하여 스레드 간 협업을 달성하십시오
1. WAIN () 및 notify ()/notifyall ()
sleep () 및 attit ()가 호출되면 잠금이 해제되지 않으며 Wait () 호출은 잠금을 해제합니다. 이러한 방식으로 다른 작업 (스레드)은 현재 객체의 잠금을 얻어 동기화 된 메소드로 들어갈 수 있습니다. wait ()에서 notify ()/notifyall ()을 통해 실행을 재개하거나 시간이 만료 될 수 있습니다.
대기 (), notify () 및 notifyall ()은 동기화 제어 메소드 또는 동기화 블록에서만 호출 할 수 있습니다. 이러한 메소드가 비동기 방법으로 호출되는 경우, 런타임에 불법 모니터 스테이트 예식 예외가 발생합니다.
2. 단일 스레드로 여러 스레드의 깨우기를 시뮬레이션합니다.
스레드 간의 협업을 시뮬레이션합니다. 게임 클래스에는 2 개의 동기화 메소드가 repay () 및 go ()가 있습니다. 플래그 시작은 현재 스레드에 대기가 필요한지 여부를 결정하는 데 사용됩니다 (). 게임 클래스의 인스턴스는 모든 Athele 클래스 인스턴스를 먼저 시작하고 대기 () 상태를 입력합니다. 일정 시간 후에는 플래그 비트를 변경하고 대기 상태의 모든 Athele 스레드를 notifyall ().
game.java
패키지 동시성; import java.util.collection; import java.util.collections; import java.util.hashset; import java.util.iterator; import java.util.set; 클래스 운동 선수 구현 {private final int id; 개인 게임 게임; 공개 운동 선수 (int id, 게임 게임) {this.id = id; this.game = 게임; } public boolean equals (Object o) {if (! (o instanceof theLete)) false를 반환합니다. 운동 선수 = (운동 선수) o; return id == Athlete.id; } public String toString () {return "athlete <" + id + ">"; } public int ashcode () {return new Integer (id) .hashcode (); } public void run () {try {game.prepare (this); } catch (InterruptedException e) {System.out.println (이 + "게임 종료"); }}} 공개 클래스 게임은 실행 가능 {private set <therlete> players = new Hashset <Therlete> (); 개인 부울 시작 = 거짓; Public Void AddPlayer (Athlete One) {players.add (One); } public void removePlayer (Athlete One) {players.remove (One); } public 컬렉션 <Athlete> getPlayers () {return Collections.unmodifiableSet (플레이어); } public void 준비 (운동 선수)가 중단 exception {System.out.println (Athlete + "Ready!"); 동기화 (this) {while (! start) 대기 (); if (start) system.out.println (Athlete + "Go!"); }} public synchronized void go () {notifyall (); } public void ready () {iterator <athlete> iter = getPlayers (). iterator (); while (iter.hasnext ()) 새 스레드 (iter.next ()). start (); } public void run () {start = false; System.out.println ( "Ready ..."); System.out.println ( "Ready ..."); System.out.println ( "Ready ..."); 준비가 된(); start = true; System.out.println ( "go!"); 가다(); } public static void main (String [] args) {게임 게임 = new Game (); for (int i = 0; i <10; i ++) game.addplayer (New Athlete (i, Game)); 새 스레드 (게임) .start (); }} 결과:
준비 ... 준비 ... 선수 <0> 준비! 선수 <10> 준비! 선수 <2> 준비! 준비! 선수 <3> 준비! 선수 <4> 준비! 선수 <5> 준비! 선수 준비! 선수 준비! 선수 <7> 준비! 선수는 <8> 선수 <9> 준비! Athlete <6 <6 <8, Athlete <8> go! go! go! 가서! 선수 <5>가! 선수 <4>가! 선수 <3>가! 선수 <2>가! 선수 <1>가! 선수 <0>가!
3. 바쁜 대기 과정을 시뮬레이션하십시오
myObject 클래스의 인스턴스는 관찰자입니다. 관찰 이벤트가 발생하면 모니터 클래스의 인스턴스에 알립니다 (플래그를 변경하는 방법). 이 모니터 클래스의 인스턴스는 대기하여 플래그 비트가 변경되는지 지속적으로 확인합니다.
BusyWaiting.java
import java.util.concurrent.timeUnit; class myObject emplements runnable {개인 모니터 모니터; public myobject (모니터 모니터) {this.monitor = 모니터; } public void run () {try {timeUnit.seconds.sleep (3); System.out.println ( "나는 가고있다"); monitor.gotMessage (); } catch (InterruptedException e) {e.printstacktrace (); }}} 클래스 모니터는 실행 가능한 {개인 휘발성 부울 go = false; public void gotMessage ()는 중단 된 exception {go = true; } public void waiting () {while (go == false); System.out.println ( "그는 갔다."); } public void run () {waiting (); }} public class busywaiting {public static void main (String [] args) {monitor monitor = new Monitor (); myObject o = 새로운 myObject (모니터); 새 스레드 (o) .start (); 새 스레드 (모니터) .start (); }} 결과:
나는 가고있다.
4. Wait () 및 notify ()를 사용하여 위의 예를 다시 작성하십시오.
다음 예제는 Wait ()를 통해 바쁜 대기 메커니즘을 대체합니다. 알림 메시지가 수신되면 현재 모니터 클래스 스레드에 알리십시오.
잠깐만 요
패키지 동시성. wait; import java.util.concurrent.timeUnit; 클래스 myObject emplements runnable {개인 모니터 모니터; public myobject (모니터 모니터) {this.monitor = 모니터; } 스레드를 정기적으로 시작하십시오
지정된 시간 후에 스레드를 시작하는 두 가지 방법이 있습니다. 먼저, java.util.concurrent.delayqueue를 통해 구현됩니다. 둘째, java.util.concurrent.scheduledthreadpoolexecutor를 통해 구현됩니다.
1. java.util.concurrent.delayqueue
클래스 Delayque는 지연이 만료 될 때만 요소를 추출 할 수있는 무한한 차단 큐입니다. 지연된 인터페이스를 요소로 구현하는 인스턴스를 허용합니다.
<< 인터페이스 >> 지연. 자바
패키지 java.util.concurrent; import java.util.*; public 인터페이스 지연 확장 <지연> {long getDelay (TimeUnit Unit);}.GetDelay ()는 주어진 시간 단위로 표현 된이 객체와 관련된 나머지 지연 시간을 반환합니다. 이 인터페이스의 구현은이 인터페이스의 getDelay 메소드와 일관된 정렬을 제공하는 비교 방법을 정의해야합니다.
Delayqueue 대기열의 헤드는 지연이 만료 된 후 가장 긴 저장 시간을 가진 지연 요소입니다. 만료는 요소의 getDelay (timeUnit.nanoseconds) 방법이 0 이하 이하 값을 반환 할 때 발생합니다.
2. 시간 지연 특성에 대한 설계 대기열
Class DelayedTasker는 Delayqueue <DelayedTask> 큐를 유지하며, 여기서 DelayedTask는 지연된 인터페이스를 구현하고 내부 클래스에 의해 정의됩니다. 외부 클래스와 내부 클래스는 실행 가능한 인터페이스를 구현합니다. 외부 클래스의 경우, 실행 방법은 정의 된 시간에 따라 큐에서 순서대로 작업을 수행하며 이러한 작업은 내부 클래스의 사례입니다. 내부 클래스의 실행 방법은 각 스레드의 특정 논리를 정의합니다.
이 디자인의 본질은 시간 특성을 가진 스레드 작업 목록을 정의하는 것이며 목록은 모든 길이 일 수 있습니다. 작업을 추가 할 때마다 시작 시간을 지정하십시오.
DelayedTasker.java
package com.zj.timedtask; import static java.util.concurrent.seconds; import static java.util.concurrent.nanoseconds; import java.util.collection; import java.util.collections; import java.util.collections; java.util.concurrent.delayed; import java.util.concurrent.executorservice; import java.util.concurrent.executors; import java.util.concurrent.timeUnit; public class DelayedTasker emplements untable <Delayqueue <지연된 queue <); public void addtask (DelayedTask e) {queue.put (e); } public void removetask () {queue.poll (); } public Collection <DelayedTask> getAllTasks () {return Collections.unmodifiableCollection (Queue); } public int getTaskquantity () {return queue.size (); } public void run () {while (! queue.isempty ()) try {queue.take (). run (); } catch (InterpruptedException e) {System.out.println ( "Interrupted"); } system.out.println ( "완성 된 DelayedTask"); } public static class DelayedTask 구현 지연, 실행 가능 {private static int counter = 0; 개인 최종 int id = 카운터 ++; 개인 최종 INT 델타; 개인 최종 긴 방아쇠; Public DelayedTask (int DelayinSeconds) {delta = DelayinSeconds; trigger = system.nanotime () + nanoseconds.convert (델타, 초); } public long getDelay (TimeUnit Unit) {return init.convert (trigger -system.nanoTime (), Nanoseconds); } public int compareto (Delayed Arg) {DelayedTask that = (DelayedTask) arg; if (trigger <that.trigger) return -1; if (trigger> that.trigger) 반환 1; 반환 0; } public void run () {// system.out.println (this)을 수행하려는 모든 것을 실행합니다. } public String toString () {return "[" + delta + "s]" + "task" + id; }} public static void main (String [] args) {random rand = new random (); executorService exec = executors.newCachedThreadPool (); DelayedTasker Tasker = 새로운 DelayedTasker (); for (int i = 0; i <10; i ++) tasker.addtask (new DelayedTask (rand.nextint (5)); exec.execute (tasker); exec.shutdown (); }} 결과:
[0S] 작업 1 [0S] 작업 2 [0S] 작업 3 [1S] 작업 6 [2S] 작업 5 [3S] 작업 8 [4S] Task 0 [4S] 작업 4 [4S] 작업 7 [4S] 작업 9finishedDelayedTask
3. java.util.concurrent.scheduledthreadpoolexecutor
이 클래스는 주어진 지연 후 작업 (스레드)을 실행하거나 정기적으로 작업을 수행하도록 예약 할 수 있습니다 (반복). 생성자에서는 스레드 풀의 크기를 알아야합니다. 주요 방법은 다음과 같습니다.
[1] 일정
공개 ScheduledFuture <?> 스케줄 (런 가능 명령, 장기 지연, TimeUnit Unit)
주어진 지연 후 일회성 작업을 생성하고 수행합니다.
지정 :
- 인터페이스의 스케줄 예약 scheduledexecutorservice;
매개 변수 :
- 명령 - 수행해야 할 과제;
-Delay- 이제부터 실행을 지연시킬 시간;
- 단위 - 지연 매개 변수의 시간 단위;
반품:
- 작업을 중단하는 ScheduledFuture를 표시하면 완료 후 get () 메소드가 NULL을 반환합니다.
[2] scheduleatfixedrate
public scheduledfuture <?> scheduleatfixedrate (
실행 가능한 명령, 긴 초기 델레이, 장기, TimeUnit 장치)
주어진 초기 지연 후 첫 번째로 활성화 된 주기적 작업을 생성하고 실행하여 후속 작업에는 주어진 기간이 있습니다. 즉, InitialDelay 이후에 시작한 다음 InitialDelay + 기간 이후에 시작된 다음 InitialDelay + 2 * 기간 등이 시작됩니다. 작업 실행이 예외가 발생하면 후속 실행이 취소됩니다. 그렇지 않으면 프로그램의 취소 또는 종료 방법을 실행하여 만 작업을 종료 할 수 있습니다. 이 작업의 실행이주기보다 오래 걸리면 후속 실행이 연기되지만 동시에는 그렇지 않습니다.
지정 :
- 스케줄링 픽시드 레이트 인터페이스에서 scheduledExecutorService;
매개 변수 :
- 명령 - 수행해야 할 과제;
-initialDelay- 첫 번째 실행의 지연 시간;
-Period- 지속적인 실행 사이의 기간;
- 단위 - 초기 텔레이 및주기 매개 변수의 시간 단위;
반품:
- 일시 중단 된 작업의 스케줄링이 완료되었음을 나타냅니다. 그리고 get () 메소드가 취소 된 후 예외가 발생합니다.
4. 시간 지연 특성을 가진 스레드 실행자 설계
클래스 ScheduLETASKED는 스레드 풀의 크기를 지정할 수있는 ScheduledThreadPooleExcutor를 연관시킵니다. 일정 방법을 통해 스레드와 지연 시간을 알고 셧다운 방법을 통해 스레드 풀을 닫으십시오. 특정 작업 (스레드)의 논리에는 특정 유연성이 있습니다 (이전 설계와 비교하여 이전 설계는 스레드의 논리를 미리 정의해야하지만 스레드의 특정 논리 설계는 상속 또는 장식으로 수정 될 수 있습니다).
scheduletasker.java
패키지 com.zj.timedtask; import java.util.concurrent.scheduledthreadpoolexecutor; import java.util.concurrent.timeUnit; public class scheduletasker {private int corepoolsize = 10; ScheduledThreadPooleExecutor 스케줄러; public scheduletasker () {scheduler = new ScheduledThreadPooleExecutor (CorePoolSize); } public scheduletasker (int rater) {corepoolsize = 수량; 스케줄러 = 새로운 ScheduledThreadPooleExecutor (CorePoolSize); } public void schedule (런 가능 이벤트, 긴 지연) {scheduler.schedule (이벤트, 지연, TimeUnit.seconds); } public void shutdown () {scheduler.shutdown (); } public static void main (String [] args) {scheduletasker tasker = new ScheduleTasker (); tasker.schedule (new runnable () {public void run () {system.out.println ( "[1s] task 1");}}, 1); tasker.schedule (new runnable () {public void run () {system.out.println ( "[2s] task 2");}}, 2); tasker.schedule (new Runnable () {public void Run () {System.out.println ( "[4S] Task 3");}}, 4); tasker.schedule (new runnable () {public void run () {public void run () {system.out.println ( "[10s] task 4"));}}, 10); tasker.shutdown (); }} 결과:
[1S] 작업 1 [2S] 작업 2 [4S] 작업 3 [10S] 작업 4 Public Void Run () {Try {TimeUnit.seconds.sleep (3); System.out.println ( "나는 가고있다"); monitor.gotMessage (); } catch (InterruptedException e) {e.printstacktrace (); }}} 클래스 모니터는 실행 가능한 {개인 휘발성 부울 go = false; Public Synchronized Void GotMessage () Throws InterruptedException {go = true; notify (); } public synchronized void avoid watching ()는 interruptedException {while (go == false) wait (); System.out.println ( "그는 갔다."); } public void run () {try {waiting (); } catch (InterruptedException e) {e.printstacktrace (); }}} 공개 클래스 대기 {public static void main (String [] args) {monitor monitor = new Monitor (); myObject o = 새로운 myObject (모니터); 새 스레드 (o) .start (); 새 스레드 (모니터) .start (); }} 결과:
나는 가고있다.