Java 멀티스레딩 구현과 락 활용 및 실행 결과 처리 기법

기본 스레드 구현: Runnable 인터페이스 활용

Runnable 인터페이스를 구현할 때 주의점: 작업 객체 인스턴스 생성은 반복문 외부에서 수행해야 합니다. 반복문 내에서 생성할 경우 락 동기화가 제대로 동작하지 않습니다.

@Controller
@RequestMapping("/task")
public class TaskProcessor implements Runnable {
    
    private final Lock taskLock = new ReentrantLock();
    private List<DataModel> dataChunk;
    private int segmentCounter = 1;
    
    public void setDataChunk(List<DataModel> dataChunk) {
        this.dataChunk = dataChunk;
    }
    
    public void setSegmentCounter(int segmentCounter) {
        this.segmentCounter = segmentCounter;
    }
    
    @Override
    public void run() {
        taskLock.lock();
        try {
            if(segmentCounter == 1) {
                Thread.sleep(5000);
            }
            System.out.println("처리 데이터 수: " + dataChunk.size());
        } catch (Exception e) {
            System.err.println("스레드 오류: " + e.getMessage());
        } finally {
            taskLock.unlock();
        }
    }

    @PostMapping("/process")
    @ResponseBody
    public int processData(@RequestBody List<DataModel> data) {
        taskLock.lock();
        try {
            TaskProcessor processor = new TaskProcessor();
            int batchSize = 2;
            
            for (int i = 0; i <= data.size(); i++) {
                if(i == batchSize * segmentCounter - 1 && i != data.size()) {
                    List<DataModel> chunk = data.subList(0, batchSize * segmentCounter);
                    processor.setDataChunk(chunk);
                    processor.setSegmentCounter(segmentCounter);
                    
                    new Thread(processor).start();
                    segmentCounter++;
                }
                
                if(i == data.size()) {
                    List<DataModel> lastChunk = data.subList(
                        batchSize * (segmentCounter - 1), data.size());
                    processor.setDataChunk(lastChunk);
                    new Thread(processor).start();
                    segmentCounter++;
                }
            }
            return segmentCounter;
        } finally {
            taskLock.unlock();
        }
    }
}

ExecutorService를 이용한 스레드 풀 관리

Runnable 상속 없이 ExecutorService로 스레드 관리:

ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(() -> {
    batchProcess(dataList, user);
});

Future를 활용한 스레드 실행 결과 획득

Callable과 Future 조합으로 스레드 실행 결과 수집:

ExecutorService executor = Executors.newFixedThreadPool(5);
List<Future<ResultObject>> resultList = new ArrayList<>();

Future<ResultObject> future = executor.submit(() -> {
    return processBatch(dataSegment, user, config);
});

resultList.add(future);

태그: java 멀티스레딩 ReentrantLock ExecutorService Future

7월 3일 06:30에 게시됨