yield()
특히 아래 예제 코드에서 Java 의 메서드 사용에 대해 약간 혼란스러워 합니다. 또한 yield ()는 ‘스레드 실행을 방지하는 데 사용됨’을 읽었습니다.
내 질문은 다음과 같습니다.
-
아래 코드는 사용할 때
yield()
와 사용하지 않을 때 모두 동일한 결과를 산출한다고 생각합니다 . 이 올바른지? -
실제로의 주요 용도는
yield()
무엇입니까? -
및 방법 과
yield()
다른 점은 무엇 입니까?join()
interrupt()
코드 예 :
public class MyRunnable implements Runnable {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();
for(int i=0; i<5; i++) {
System.out.println("Inside main");
}
}
public void run() {
for(int i=0; i<5; i++) {
System.out.println("Inside run");
Thread.yield();
}
}
}
위의 코드를 사용하거나 사용하지 않고 동일한 출력을 얻습니다 yield()
.
Inside main
Inside main
Inside main
Inside main
Inside main
Inside run
Inside run
Inside run
Inside run
Inside run
답변
출처 : http://www.javamex.com/tutorials/threads/yield.shtml
윈도우
핫스팟 구현에서
Thread.yield()
작동 방식은 Java 5와 Java 6간에 변경되었습니다.Java 5
Thread.yield()
에서 Windows API 호출을 호출Sleep(0)
합니다. 이것은 현재 스레드의 퀀텀 을 지우고 우선 순위 수준 에 대해 큐 의 끝에 넣는 특수 효과가 있습니다 . 즉, 동일한 우선 순위 (및 더 높은 우선 순위의 스레드)의 모든 실행 가능한 스레드는 양보 된 스레드가 다음에 CPU 시간이 주어지기 전에 실행될 기회를 갖게됩니다. 결국 일정이 다시 잡히면 완전한 전체 퀀텀으로 돌아 오지만 양보 시점부터 남은 퀀텀을 “이월”하지는 않습니다. 이 동작은 수면 스레드가 일반적으로 1 개의 양자 값 (실제로는 10 또는 15ms 틱의 1/3)을 잃는 0이 아닌 수면과는 약간 다릅니다.Java 6에서는이 동작이 변경되었습니다. Hotspot VM은 이제
Thread.yield()
WindowsSwitchToThread()
API 호출을 사용하여 구현
됩니다 . 이 호출은 현재 스레드 가 전체 퀀텀이 아닌 현재 타임 슬라이스를 포기하도록합니다 . 이는 다른 스레드의 우선 순위에 따라 양보 스레드가 나중에 한 인터럽트 기간으로 다시 예약 될 수 있음을 의미합니다 . ( 타임 슬라이스에 대한 자세한 정보는 스레드 스케줄링 섹션을 참조하십시오 .)리눅스
Linux에서 Hotspot은 단순히
sched_yield()
. 이 호출의 결과는 Windows보다 약간 다르며 더 심각 할 수 있습니다.
- 양보 된 스레드는 다른 모든 스레드가 CPU 조각을 가질 때까지 다른 CPU 조각을 얻지 못합니다 .
- (적어도 커널 2.6.8 이상에서는) 스레드가 양보했다는 사실은 최근 CPU 할당에 대한 스케줄러의 휴리스틱에 의해 암시 적으로 고려됩니다. 따라서 묵시적으로 일정을 잡을 때 양보 한 스레드에 더 많은 CPU를 할당 할 수 있습니다. 미래.
( 우선 순위 및 스케줄링 알고리즘에 대한 자세한 내용 은 스레드 스케줄링 섹션을 참조하십시오 .)
언제 사용
yield()
합니까?나는 실질적으로 결코 말하지 않을 것이다. 그 동작은 표준 적으로 정의되어 있지 않으며 일반적으로 yield ()로 수행하려는 작업을 수행하는 더 좋은 방법이 있습니다.
- CPU의 일부만 사용 하려는 경우 스레드가 마지막 처리 청크에서 사용한 CPU 양을 추정 한 다음 보상을 위해 일정 시간 동안 잠자기 때문에보다 제어 가능한 방식으로이를 수행 할 수 있습니다 . 슬립 () 메소드;
- 있는 거 당신이 경우 프로세스 또는 자원에 대한 대기 가능하게 완료 또는,이보다 효율적으로 같은 사용하여 같은 이러한 목표를 달성 할 수있는 방법이다 ()에 가입 완료하기 위해 다른 스레드를 기다릴의 사용 / 대기를 통지 하나 개의 스레드를 허용하는 메커니즘을 태스크가 완료되었음을 다른 사람에게 알리거나 이상적으로는 Semaphore 또는 블로킹 큐 와 같은 Java 5 동시성 구성 중 하나를 사용하여 신호를 보냅니다 .
답변
나는 질문이 현상금과 함께 다시 활성화되어 이제 실제 용도가 무엇인지 묻습니다 yield
. 내 경험에서 예를 들어 보겠습니다.
아시다시피, yield
호출 스레드가 실행중인 프로세서를 강제로 포기하여 다른 스레드가 실행되도록 예약 할 수 있습니다. 이것은 현재 스레드가 현재 작업을 완료했지만 대기열의 맨 앞으로 빠르게 돌아가서 일부 조건이 변경되었는지 확인하려는 경우에 유용합니다. 이것은 조건 변수와 어떻게 다릅니 까? yield
스레드가 실행 상태로 훨씬 더 빨리 돌아갈 수 있도록합니다. 조건 변수를 기다릴 때 스레드는 일시 중단되고 다른 스레드가 계속해야한다는 신호를 보낼 때까지 기다려야합니다.yield
기본적으로 “다른 스레드가 실행되도록 허용하지만 내 상태에서 무언가가 매우 빠르게 변경 될 것으로 예상하면 곧 다시 작업 할 수 있습니다.”라고 말합니다. 이는 상태가 빠르게 변경 될 수 있지만 스레드를 일시 중단하면 성능이 크게 저하되는 바쁜 회전을 나타냅니다.
그러나 충분히 옹알이는 구체적인 예가 있습니다. 파면 평행 패턴입니다. 이 문제의 기본 사례는 0과 1로 채워진 2 차원 배열에서 1의 개별 “섬”을 계산하는 것입니다. “섬”은 수직 또는 수평으로 서로 인접한 셀 그룹입니다.
1 0 0 0
1 1 0 0
0 0 0 1
0 0 1 1
0 0 1 1
여기에 1로 구성된 두 개의 섬이 있습니다. 왼쪽 상단과 오른쪽 하단입니다.
간단한 해결책은 전체 배열에 대한 첫 번째 패스를 만들고 1 값을 증분 카운터로 대체하여 끝까지 각 1이 행 주요 순서의 시퀀스 번호로 대체되도록하는 것입니다.
1 0 0 0
2 3 0 0
0 0 0 4
0 0 5 6
0 0 7 8
다음 단계에서 각 값은 자신과 이웃 값 사이의 최소값으로 대체됩니다.
1 0 0 0
1 1 0 0
0 0 0 4
0 0 4 4
0 0 4 4
이제 두 개의 섬이 있음을 쉽게 확인할 수 있습니다.
병렬로 실행하려는 부분은 최소값을 계산하는 단계입니다. 너무 자세히 설명하지 않고 각 스레드는 인터리브 방식으로 행을 가져오고 위의 행을 처리하는 스레드가 계산 한 값에 의존합니다. 따라서 각 스레드는 이전 행을 처리하는 스레드보다 약간 뒤쳐 져야하지만 적절한 시간 내에 유지되어야합니다. 자세한 내용과 구현은 이 문서 에서 직접 제공 합니다 . 의 사용법 sleep(0)
은 yield
.
이 경우 yield
각 스레드를 강제로 일시 중지하기 위해 사용되었지만 인접한 행을 처리하는 스레드가 그 동안 매우 빠르게 진행되므로 조건 변수는 비참한 선택이 될 것입니다.
보시다시피, yield
상당히 세분화 된 최적화입니다. 엉뚱한 장소에서 사용하는 경우 (예 : 거의 변경되지 않는 상태에서 기다리는 경우) CPU를 과도하게 사용하게됩니다.
긴 옹알이에 대해 죄송합니다.
답변
yield()
, interrupt()
및 join()
-일반적으로 Java뿐만 아니라 차이점에 대해 :
- yielding : 말 그대로 ‘양보하다’는 것은 놓아주고, 포기하고, 항복하는 것을 의미합니다. 양보하는 스레드는 대신 다른 스레드가 예약되도록 허용 할 의사가있는 운영 체제 (또는 가상 머신 또는 그렇지 않은 것)를 알려줍니다. 이것은 너무 비판적인 일을하고 있지 않다는 것을 나타냅니다. 그러나 힌트 일 뿐이며 효과가 보장되지는 않습니다.
- joining : 여러 스레드가 일부 핸들, 토큰 또는 엔터티에 ‘join’할 때 모든 다른 관련 스레드가 실행을 완료 할 때까지 (전체적으로 또는 해당 결합까지) 기다립니다. 이는 많은 스레드가 모두 작업을 완료했음을 의미합니다. 그런 다음 이러한 스레드 각각은 다른 작업을 계속하도록 예약 할 수 있으며 이러한 모든 작업이 실제로 완료되었다고 가정 할 수 있습니다. (SQL 조인과 혼동하지 마십시오!)
- interruption : 한 스레드가 대기 중이거나 대기 중이거나 참여중인 다른 스레드를 ‘포킹’하는 데 사용되므로 중단되었음을 표시하면서 다시 계속 실행되도록 예약됩니다. (하드웨어 인터럽트와 혼동하지 마십시오!)
특히 Java의 경우
-
합류:
Thread.join을 사용하는 방법? (여기 StackOverflow에서)
-
굽힐 수 있는:
-
중단 :
Thread.interrupt ()가 사악합니까? (여기 StackOverflow에서)
답변
첫째, 실제 설명은
현재 실행중인 스레드 개체가 일시적으로 일시 중지되고 다른 스레드가 실행되도록합니다.
이제 run
새 스레드 의 메서드 가 실행되기 전에 메인 스레드가 루프를 5 번 실행할 가능성이 높 으므로 모든 호출 yield
은 메인 스레드의 루프가 실행 된 후에 만 발생합니다.
join
호출되는 스레드가 join()
실행을 완료 할 때까지 현재 스레드를 중지합니다 .
interrupt
호출되는 스레드를 중단하여 InterruptedException을 발생시킵니다 .
yield
다른 스레드로 컨텍스트 전환을 허용하므로이 스레드는 프로세스의 전체 CPU 사용량을 소비하지 않습니다.
답변
현재 답변이 오래되었으며 최근 변경 사항에 따라 수정해야합니다.
6-9 이후 Java 버전간에 실질적인 차이 는 없습니다 Thread.yield()
.
TL; DR;
OpenJDK 소스 코드 ( http://hg.openjdk.java.net/ ) 에 기반한 결론입니다 .
USDT 프로브 (시스템 추적 정보는 dtrace 가이드에 설명되어 있음 ) 및 JVM 속성의 HotSpot 지원을 고려하지 않으면의 ConvertYieldToSleep
소스 코드 yield()
는 거의 동일합니다. 아래 설명을 참조하십시오.
자바 9 :
Thread.yield()
OS 별 메서드 호출 os::naked_yield()
:
Linux에서 :
void os::naked_yield() {
sched_yield();
}
Windows의 경우 :
void os::naked_yield() {
SwitchToThread();
}
Java 8 이하 :
Thread.yield()
OS 별 메서드 호출 os::yield()
:
Linux에서 :
void os::yield() {
sched_yield();
}
Windows의 경우 :
void os::yield() { os::NakedYield(); }
보시다시피 Thread.yeald()
Linux에서는 모든 Java 버전이 동일합니다. JDK 8에서
Windows를 살펴 보겠습니다 os::NakedYield()
.
os::YieldResult os::NakedYield() {
// Use either SwitchToThread() or Sleep(0)
// Consider passing back the return value from SwitchToThread().
if (os::Kernel32Dll::SwitchToThreadAvailable()) {
return SwitchToThread() ? os::YIELD_SWITCHED : os::YIELD_NONEREADY ;
} else {
Sleep(0);
}
return os::YIELD_UNKNOWN ;
}
Win32 API SwitchToThread()
메서드 의 존재에 대한 추가 검사에서 Java 9와 Java 8의 차이점 . Java 6에도 동일한 코드가 있습니다 . JDK 7
의 소스 코드 os::NakedYield()
는 약간 다르지만 동작은 같습니다.
os::YieldResult os::NakedYield() {
// Use either SwitchToThread() or Sleep(0)
// Consider passing back the return value from SwitchToThread().
// We use GetProcAddress() as ancient Win9X versions of windows doen't support SwitchToThread.
// In that case we revert to Sleep(0).
static volatile STTSignature stt = (STTSignature) 1 ;
if (stt == ((STTSignature) 1)) {
stt = (STTSignature) ::GetProcAddress (LoadLibrary ("Kernel32.dll"), "SwitchToThread") ;
// It's OK if threads race during initialization as the operation above is idempotent.
}
if (stt != NULL) {
return (*stt)() ? os::YIELD_SWITCHED : os::YIELD_NONEREADY ;
} else {
Sleep (0) ;
}
return os::YIELD_UNKNOWN ;
}
SwitchToThread()
Windows XP 및 Windows Server 2003부터 사용할 수있는 방법 으로 인해 추가 검사가 삭제되었습니다 ( msdn 참고 사항 참조 ).
답변
실제로 yield ()의 주요 용도는 무엇입니까?
Yield는 CPU에 현재 스레드를 중지하고 더 높은 우선 순위로 스레드 실행을 시작할 수 있음을 제안합니다. 즉, 더 중요한 스레드를위한 공간을 남겨두기 위해 현재 스레드에 낮은 우선 순위 값을 할당합니다.
아래 코드는 yield ()를 사용할 때와 사용하지 않을 때 모두 동일한 출력을 생성한다고 생각합니다. 이 올바른지?
아니요, 둘은 다른 결과를 생성합니다. yield ()가 없으면 스레드가 제어를 받으면 ‘Inside run’루프가 한 번에 실행됩니다. 그러나 yield ()를 사용하면 스레드가 제어를 받으면 ‘Inside run’을 한 번 인쇄 한 다음 제어권을 다른 스레드에 넘겨줍니다. 보류중인 스레드가 없으면이 스레드가 다시 재개됩니다. 따라서 “Inside run”이 실행될 때마다 실행할 다른 스레드를 찾고 사용 가능한 스레드가 없으면 현재 스레드가 계속 실행됩니다.
yield ()는 join () 및 interrupt () 메서드와 어떤 점에서 다릅니 까?
yield ()는 다른 중요한 스레드에 공간을 제공하고 join ()은 다른 스레드가 실행을 완료 할 때까지 대기하며 interrupt ()는 현재 실행중인 스레드를 중단하여 다른 작업을 수행합니다.
답변
Thread.yield()
스레드가 “실행 중”상태에서 “실행 가능”상태로 이동합니다. 참고 : 스레드가 “대기 중”상태가되지는 않습니다.