자바 는 어떻게 스 레 드 간 통신 을 실현 합 니까?
본 논문 과 관련 된 지식 점:
다음은 자바 에서 어떤 방법 으로 스 레 드 간 통신 을 실현 하 는 지 몇 가지 예 를 들 어 착안점 으로 설명 하 겠 습 니 다.
어떻게 두 라인 을 순서대로 집행 합 니까?
두 개의 스 레 드 가 있다 고 가정 하면 하 나 는 스 레 드 A 이 고 다른 하 나 는 스 레 드 B 이 며 두 스 레 드 는 각각 1 - 3 개의 숫자 를 순서대로 인쇄 하면 된다.코드 를 살 펴 보 겠 습 니 다.
private static void demo1() {
Thread A = new Thread(new Runnable() {
@Override
public void run() {
printNumber("A");
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
printNumber("B");
}
});
A.start();
B.start();
}
그 중의 printNumber (String) 는 다음 과 같이 1, 2, 3 개의 숫자 를 순서대로 인쇄 합 니 다.
private static void printNumber(String threadName) {
int i=0;
while (i++ < 3) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(threadName + " print: " + i);
}
}
이때 우리 가 얻 은 결 과 는:
B print: 1
A print: 1
B print: 2
A print: 2
B print: 3
A print: 3
A 와 B 가 동시에 인쇄 된 것 을 볼 수 있다.
그렇다면 B 가 A 가 모두 인쇄 된 후에 인쇄 를 시작 하 기 를 원한 다 면?우 리 는 thread. join () 방법 을 이용 할 수 있 습 니 다. 코드 는 다음 과 같 습 니 다.
private static void demo2() {
Thread A = new Thread(new Runnable() {
@Override
public void run() {
printNumber("A");
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("B 开始等待 A");
try {
A.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
printNumber("B");
}
});
B.start();
A.start();
}
얻 은 결 과 는 다음 과 같다.
B 开始等待 A
A print: 1
A print: 2
A print: 3
B print: 1
B print: 2
B print: 3
그래서 우 리 는 A. join () 방법 을 볼 수 있 고 B 는 A 가 실 행 될 때 까지 기다 릴 수 있 습 니 다.
그럼 어떻게
两
개 스 레 드 는 지 정 된 방식 에 따라 질서 있 게 교차 운행 합 니까?아니면 위의 예 입 니까? 저 는 지금 A 가 1 을 인쇄 한 후에 B 에 게 1, 2, 3 을 인쇄 하 게 하고 마지막 에 A 로 돌아 가 2, 3 을 계속 인쇄 하 기 를 바 랍 니 다.이런 수요 에서 Thread. join () 은 이미 만족 할 수 없 음 이 분명 하 다.우 리 는 실행 순 서 를 조절 하기 위해 서 좀 더 세밀 한 자물쇠 가 필요 하 다.
여기 서 우 리 는 object. wait () 와 object. notify () 두 가지 방법 으로 실현 할 수 있 습 니 다.코드 는 다음 과 같 습 니 다:
/**
* A 1, B 1, B 2, B 3, A 2, A 3
*/
private static void demo3() {
Object lock = new Object();
Thread A = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
System.out.println("A 1");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A 2");
System.out.println("A 3");
}
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
System.out.println("B 1");
System.out.println("B 2");
System.out.println("B 3");
lock.notify();
}
}
});
A.start();
B.start();
}
인쇄 결 과 는 다음 과 같 습 니 다.
A 1
A waiting…
B 1
B 2
B 3
A 2
A 3
바로 우리 가 원 하 는 결과 다.
그렇다면 이 과정 에서 무슨 일이 일 어 났 을 까?
더 잘 이해 하기 위해 서 나 는 위의 코드 에 로 그 를 더 해서 독자 들 이 쉽게 볼 수 있 도록 했다.
private static void demo3() {
Object lock = new Object();
Thread A = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("INFO: A 等待锁 ");
synchronized (lock) {
System.out.println("INFO: A 得到了锁 lock");
System.out.println("A 1");
try {
System.out.println("INFO: A 准备进入等待状态,放弃锁 lock 的控制权 ");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("INFO: 有人唤醒了 A, A 重新获得锁 lock");
System.out.println("A 2");
System.out.println("A 3");
}
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("INFO: B 等待锁 ");
synchronized (lock) {
System.out.println("INFO: B 得到了锁 lock");
System.out.println("B 1");
System.out.println("B 2");
System.out.println("B 3");
System.out.println("INFO: B 打印完毕,调用 notify 方法 ");
lock.notify();
}
}
});
A.start();
B.start();
}
인쇄 결 과 는 다음 과 같 습 니 다.
INFO: A 等待锁
INFO: A 得到了锁 lock
A 1
INFO: A 准备进入等待状态,调用 lock.wait() 放弃锁 lock 的控制权
INFO: B 等待锁
INFO: B 得到了锁 lock
B 1
B 2
B 3
INFO: B 打印完毕,调用 lock.notify() 方法
INFO: 有人唤醒了 A, A 重新获得锁 lock
A 2
A 3
네 개의 스 레 드 A B C D, 그 중에서 D 는 A B C 가 모두 실 행 된 후에 야 실 행 됩 니 다. 그리고 A B C 는 동기 화 되 어 실 행 됩 니 다.
처음에 우 리 는 thread. join () 을 소 개 했 습 니 다. 한 스 레 드 가 다른 스 레 드 가 실 행 된 후에 계속 실행 할 수 있 습 니 다. 그러면 우 리 는 D 스 레 드 에서 차례대로 join A B C 를 실행 할 수 있 습 니 다. 그러나 이것 은 A B C 가 순서대로 실행 해 야 합 니 다. 우리 가 원 하 는 것 은 이 세 사람 이 동시에 실행 할 수 있 도록 하 는 것 입 니 다.
또는 우리 가 달성 하고 자 하 는 목적 은 A. B. C 세 개의 스 레 드 가 동시에 운행 되 고 각자 독립 적 으로 운행 한 후에 D 에 게 알 리 는 것 이다.D 에 게 는 A, B, C 가 모두 실행 되면 D 가 다시 운행 을 시작한다.이런 상황 에 대해 우 리 는 Countdown Latch 를 이용 하여 이런 통신 방식 을 실현 할 수 있다.그것 의 기본 용법 은:
구현 코드 는 다음 과 같 습 니 다:
private static void runDAfterABC() {
int worker = 3;
CountDownLatch countDownLatch = new CountDownLatch(worker);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("D is waiting for other three threads");
try {
countDownLatch.await();
System.out.println("All done, D starts working");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
for (char threadName='A'; threadName <= 'C'; threadName++) {
final String tN = String.valueOf(threadName);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(tN + " is working");
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(tN + " finished");
countDownLatch.countDown();
}
}).start();
}
}
다음은 실행 결과 입 니 다.
D is waiting for other three threads
A is working
B is working
C is working
A finished
C finished
B finished
All done, D starts working
사실은 간단하게 말 하면 Countdown Latch 는 카운터 입 니 다. 우 리 는 초기 계수 값 을 3 으로 설정 합 니 다. D 가 실 행 될 때 먼저 countDownlatch. await () 를 호출 하여 카운터 값 이 0 인지 확인 하고 0 이 아니라면 대기 상 태 를 유지 합 니 다.A B C 가 각각 실행 되면 countDownlatch. countDown () 을 이용 하여 카운터 1 을 줄 이 고 세 개 를 모두 실행 한 후에 카운터 가 0 으로 줄어든다.이 때 D 를 즉시 터치 하 는 await () 실행 이 끝나 면 계속 아래로 실행 합 니 다.
따라서 Count Downlatch 는 한 라인 에서 여러 라인 을 기다 리 는 경우 에 적용 된다.
세 선 수 는 각자 준비 하고 세 사람 이 모두 준비 가 다 된 후에 다시 함께 뛴다.
위 는 스 레 드 A B C 에 대해 각자 준 비 를 시작 하고 세 사람 이 모두 준 비 를 마 친 다음 에 동시에 운행 하 는 이미 지 를 비유 한 것 이다.즉, 하나의 스 레 드 간 에 서로 기다 리 는 효 과 를 실현 하려 면 어떻게 실현 해 야 합 니까?
위의 Count Downlatch 는 거꾸로 계산 할 수 있 지만, 계산 이 끝나 면 한 라인 의 await () 만 응답 을 받 을 수 있 으 며, 여러 라인 이 동시에 터치 할 수 없습니다.
스 레 드 간 에 서로 기다 리 는 수 요 를 실현 하기 위해 서 우 리 는 Cyclic Barrier 데이터 구 조 를 이용 할 수 있 습 니 다. 그의 기본 적 인 용법 은:
실현 코드 는 다음 과 같다. 세 명의 달리기 선수 가 각자 준 비 를 다 한 후에 다른 사람 을 기다 리 고 모두 준 비 를 한 후에 야 달리 기 를 시작 할 것 이 라 고 구상 한다.
private static void runABCWhenAllReady() {
int runner = 3;
CyclicBarrier cyclicBarrier = new CyclicBarrier(runner);
final Random random = new Random();
for (char runnerName='A'; runnerName <= 'C'; runnerName++) {
final String rN = String.valueOf(runnerName);
new Thread(new Runnable() {
@Override
public void run() {
long prepareTime = random.nextInt(10000) + 100;
System.out.println(rN + " is preparing for time: " + prepareTime);
try {
Thread.sleep(prepareTime);
} catch (Exception e) {
e.printStackTrace();
}
try {
System.out.println(rN + " is prepared, waiting for others");
cyclicBarrier.await(); // 当前运动员准备完毕,等待别人准备好
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(rN + " starts running"); // 所有运动员都准备好了,一起开始跑
}
}).start();
}
}
인쇄 결 과 는 다음 과 같 습 니 다.
A is preparing for time: 4131
B is preparing for time: 6349
C is preparing for time: 8206
A is prepared, waiting for others
B is prepared, waiting for others
C is prepared, waiting for others
C starts running
A starts running
B starts running
하위 스 레 드 가 어떤 임 무 를 완성 한 후, 얻 은 결 과 를 주 스 레 드 에 되 돌려 줍 니 다.
실제 개발 에서 우 리 는 자주 서브 스 레 드 를 만들어 서 시간 이 걸 리 는 임 무 를 한 다음 에 작업 수행 결 과 를 메 인 스 레 드 에 전송 하여 사용 해 야 합 니 다. 이런 상황 은 자바 에서 어떻게 실현 해 야 합 니까?
스 레 드 생 성 을 돌 이 켜 보면 runnable 대상 을 Thread 에 전달 합 니 다.Runnable 정 의 는 다음 과 같 습 니 다.
public interface Runnable {
public abstract void run();
}
run () 이 실 행 된 후에 어떤 결과 도 되 돌려 주지 않 는 것 을 볼 수 있 습 니 다.그럼 결과 로 돌아 가 고 싶다 면?다른 인터페이스 클래스 Callable 을 사용 할 수 있 습 니 다.
@FunctionalInterface
public interface Callable {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
이 를 통 해 알 수 있 듯 이 Callable 의 가장 큰 차 이 는 리 턴 모델 V 결과 다.
그렇다면 다음 문 제 는 하위 스 레 드 의 결 과 를 어떻게 되 돌려 주 느 냐 하 는 것 이다.자바 에 서 는 Callable 에 맞 춰 사용 되 는 클래스 가 있 습 니 다. Future Task 입 니 다. 하지만 결 과 를 얻 는 get 방법 은 메 인 스 레 드 를 막 을 수 있 습 니 다.
예 를 들 어 우 리 는 서브 스 레 드 로 하여 금 1 에서 100 까지 계산 하 게 하고 계산 한 결 과 를 메 인 스 레 드 로 되 돌려 주 고 싶다.
private static void doTaskWithResultInWorker() {
Callable callable = new Callable() {
@Override
public Integer call() throws Exception {
System.out.println("Task starts");
Thread.sleep(1000);
int result = 0;
for (int i=0; i<=100; i++) {
result += i;
}
System.out.println("Task finished and return result");
return result;
}
};
FutureTask futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();
try {
System.out.println("Before futureTask.get()");
System.out.println("Result: " + futureTask.get());
System.out.println("After futureTask.get()");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
인쇄 결 과 는 다음 과 같 습 니 다.
Before futureTask.get()
Task starts
Task finished and return result
Result: 5050
After futureTask.get()
주 스 레 드 가 future Task. get () 방법 을 호출 할 때 주 스 레 드 를 막 는 것 을 볼 수 있 습 니 다.그리고 Callable 내부 에서 실행 을 시작 하고 연산 결 과 를 되 돌려 줍 니 다.이 때 future Task. get () 이 결 과 를 얻 고 메 인 스 레 드 가 다시 실 행 됩 니 다.
여기 서 우 리 는 Future Task 와 Callable 을 통 해 메 인 스 레 드 에서 하위 스 레 드 의 연산 결 과 를 직접 얻 을 수 있 고 메 인 스 레 드 를 막 아야 한 다 는 것 을 배 울 수 있 습 니 다.물론 메 인 스 레 드 를 막 지 않 으 려 면 Executor Service 를 이용 하여 Future Task 를 스 레 드 탱크 에 넣 어 관리 하고 실행 하 는 것 을 고려 할 수 있 습 니 다.
작은 매듭
다 중 스 레 드 는 현대 언어의 공 통 된 특성 이 고 스 레 드 간 통신, 스 레 드 동기 화, 스 레 드 안전 은 매우 중요 한 화제 이다.본 고 는 자바 의 스 레 드 간 통신 에 대해 대체적인 설명 을 했 고 그 다음 에 스 레 드 동기 화, 스 레 드 안전 에 대해 서도 설명 할 것 이다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.