JAVA에서 비동기 처리를 위해 스레드를 사용할 수 있다.
다른 서버로 요청을 보낼 때 비동기 방식을 사용한다면 아래와 같이 작성하면 된다.
public void request() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
//코드 작성
}
});
thread.start();
}
하지만 병렬작업 처리량이 많아지면 성능이 저하되는데, 이를 막기 위해서 스레드 풀을 사용해야 한다.
스레드풀은 스레드 개수를 미리 정해 놓고, 작업 큐에 들어오는 요청을 미리 생성해 놓은 스레드들에게 할당하는 방식이다.
JDK 1.5부터는 java.util.concurrent Package에 ExecutorService 인터페이스와 Executors 클래스를 제공하고 있다.
static ExecutorService executorService = Executors.newFixedThreadPool(10);
public void request() {
executorService.submit(new Runnable() {
@Override
public void run() {
//코드 작성
}
});
}
위 문제를 해결하기 위해 스레드 풀을 적용했지만 스레드 풀도 단점이 있다.
100개의 스레드를 만들어 놓았지만 실제로 10개의 요청만 들어온다면 나머지 90개의 스레드는 메모리만 차지하게 되고.. 메모리 낭비가 발생한다.
또한 작업 완료 소요 시간이 다를 경우 유휴 시간이 발생하게 된다.
예를 들어 A, B, C, D 스레드가 있을 경우 A, B는 이미 처리가 끝났는데 C, D는 완료되지 않았을 경우 A, B 스레드는 C, D의 작업이 끝날 때까지 가다리게게 된다.
이를 방지 하기 위해 JDK 1.7부터는 forJoinPool를 제공하며, 아래 블로그에서 자세한 설명을 볼 수 있다! (설명을 쉽게 잘 해놨으므로 꼭 보기!) https://hamait.tistory.com/612
본인은 대량의 요청을 병렬처리할 때 아래와 같은 방법을 사용했다.
- RequestService.java
RequestThread requestThread = null;
ArrayList threadList = new ArrayList();
int reqThreadCnt = 3;
for(int i = 0 ; i < reqList.size() ; i++) {
reqVo = reqList.get(i);
requestThread = new RequestThread(this, reqVo);
requestThread.start(); //Thread 실행, RequestThread의 run 메소드가 실행됨.
threadList.add(requestThread);
//이 부분 예시는 아래 참고하기!
if( ( ( i + 1) % this.reqThreadCnt == 0 ) || ( i == (reqList.size() - 1 ) ) ){
super.checkThreadListAlive(threadList, 3000);
threadList.clear();
}
}
- reqList = 요청을 보낼 데이터 리스트
- reqThreadCnt = 한번에 실행되는 스레드 개수
- RequestThread= 스레드
- 예시
- 데이터가 1~10까지 10개가 있고, 스레드 개수가 3개라면 먼저 1,2,3 데이터 처리
- 1,2,3 데이터 처리 완료되면 4,5,6 데이터 처리
- 4,5,6 데이터 처리 완료되면 7,8,9 데이터 처리
- 7,8,9 데이터 처리 완료되면 10 데이터 처리
- ((i + 1) % reqThreadCnt) == 0
- 3(i=2), 6(i=5),9(i=8)데이터 처리할 때 위 조건이 ture → checkThreadListAlive 실행
- i == (reqList.size() - 1 ): 10 데이터 처리할 때 위 조건이 true
- 마지막 데이터인지 체크해서 스레드 개수만큼 안했어도 checkThreadListAlive 실행되게 하기 위한 조건
- 3(i=2), 6(i=5),9(i=8)데이터 처리할 때 위 조건이 ture → checkThreadListAlive 실행
- RequestThread.java
/**
* 하나의 요청을 처리하는 Thread
*/
public class RequestThread extends Thread{
public Logger logger = Logger.getLogger(RequestThread.class.getName());
private RequestService requestService;
private TestVo req ;
public RequestThread(RequestService requestService, TestVo req) {
this.requestService = requestService;
this.req = req;
}
@Override
public void run() {
this.requestService.request(this.req);
}
}
- checkThreadAlive 메소드
/**
* Thread가 살아있는지 check
* @param threadList
* @param sleepMIS
*/
public void checkThreadAlive(ArrayList threadList, int sleepMIS) {
boolean isAlive = true;
while(isAlive) {
try {
Thread.sleep(sleepMIS);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread thread = null;
for(int i = 0 ; i < threadList.size(); i++) {
thread = (Thread)threadList.get(i);
if(thread != null && thread.isAlive()) {
isAlive = true;
break;
}
thread = null;
isAlive = false;
}
}
}
- sleepMIS : 3000 → 3초마다 스레드가 살아있는지 체크
=> 스레드가 죽었다는건 프로세스 완료 했다는 것!
- 1,2,3 처리하는 스레드 3개가 threadList에 담겨있음
=> for문 돌면서 스레드 3개가 다 종료할 때까지 while문이 도는 것!
'개발 > JAVA' 카테고리의 다른 글
[JAVA] KeyStore에 Root 인증서 등록하기(SSLHandshakeException, PKIXException) (1) | 2022.05.18 |
---|---|
[JAVA] Runnable 과 Thread의 차이 (0) | 2022.05.16 |
[JAVA] java.lang.NoSuchMethodError: org.apache.logging.log4j.Logger.error(Ljava/lang/String;Ljava/lang/Object;)V] (0) | 2022.03.25 |
[JAVA] HttpClient 로그 남기기, 로그 레벨 제어하기 (0) | 2022.03.25 |
[JAVA] jackson -JsonInclude 속성 (null, empty등의 데이터 제외하기) (0) | 2022.03.11 |
댓글