여러 스레드가 동시에 접근하는 경우 동시성 컬렉션{ java.util.concurrent 패키지} 및 동시성 변수 volatile을 사용하는 것이 안전하다.
ConcurrentLinkedQueue : QUEUE 상속, concuurent 패키지 / 여러 스레드에 의해 자료구조가 수정될 수 있음
volatile : 동시에 실행되는 여러 스레드에 의해 필드가 수정될 수 있음을 나타냄
package thread.control.printer;
import java.util.Queue;
import java.util.Scanner;
import java.util.concurrent.ConcurrentLinkedQueue;
import static util.MyLogger.log;
import static util.ThreadUtils.sleep;
public class MyPrinterV2 {
public static void main(String[] args) {
Printer printer = new Printer();
Thread printerThread = new Thread(printer, "printer");
printerThread.start();
Scanner userInput = new Scanner(System.in);
while(true) {
log("프린터할 문서를 입력하세요. 종료 (q) : ");
String input = userInput.nextLine();
if (input.equals("q")) {
printerThread.interrupt();
break;
}
printer.addJob(input);
}
}
public void addJob(String input) {
jobQueue.offer(input);
}
static class Printer implements Runnable {
Queue<String> jobQueue = new ConcurrentLinkedQueue<>();
// 여러 스레드가 동시에 접근하는 경우에는
// 컬렉션 프레임워크에서 제공하는 자료구조는 안전하지 않다.
// 동시성 컬렉션 사용
// Queue의 경우 ConcurrentLinkedQueue가 안전하다.
@Override
public void run() {
while(!Thread.interrupted()) {
if(jobQueue.isEmpty()) {
continue;
}
try {
String job = jobQueue.poll();
log("출력 시작 : " + job + ", 대기 문서 : " + jobQueue);
Thread.sleep(3000);
log("출력완료");
} catch (InterruptedException e) {
log("인터럽트");
break;
}
}
log("프린터 종료");
}
}
}
[ Thread.yield() ]
sleep을 지정한 경우는 CPU실행이 잠깐 멈추고, timed-waiting 상태로 들어가는 반면,
Thread.yield()로 설정된 스레드는 스케쥴링내에서 후순위로 지정되어,
다른 스레드가 더 선순위에 올라갈 수 있도록 양보함 ( 양보할 스레드가 없으면 양보 하지 않고 실행)
예로 들어 반복적인 실행을 위해 반복문을 포함하는 경우가 많은데, 이 반복문들이 무의미한 반복을 하는 경우에 사용
※ Thread.join()은 다른 스레드를 아예 기다리는 것
※ Runnable : Ready(CPU 실행될 수 있는 스케쥴링 대기) + Running(CPU에서 실행)
package thread.control.yield;
import java.util.Queue;
import java.util.Scanner;
import java.util.concurrent.ConcurrentLinkedQueue;
import static util.MyLogger.log;
public class MyPrinterV3 {
public static void main(String[] args) {
Printer printer = new Printer();
Thread printerThread = new Thread(printer, "printer");
printerThread.start();
Scanner userInput = new Scanner(System.in);
while(true) {
log("프린터할 문서를 입력하세요. 종료 (q) : ");
String input = userInput.nextLine();
if (input.equals("q")) {
printerThread.interrupt();
break;
}
printer.addJob(input);
}
}
static class Printer implements Runnable {
Queue<String> jobQueue = new ConcurrentLinkedQueue<>();
@Override
public void run() {
while(!Thread.interrupted()) {
if(jobQueue.isEmpty()) {
Thread.yield();
// 추가 해당 if문의 Runnable 우선순위로
// 뒤로 넘겨서 CPU 자원을 덜 쓸 수 있다.
continue;
}
try {
String job = jobQueue.poll();
log("출력 시작 : " + job + ", 대기 문서 : " + jobQueue);
Thread.sleep(3000);
log("출력완료");
} catch (InterruptedException e) {
log("인터럽트");
break;
}
}
log("프린터 종료");
}
public void addJob(String input) {
jobQueue.offer(input);
}
}
}
'JAVA' 카테고리의 다른 글
동시성 문제와 동기화(synchronized) (0) | 2024.11.24 |
---|---|
멀티스레드 메모리 접근 방식/메모리 가시성/happens-before (0) | 2024.11.21 |
스레드 인터럽트 (0) | 2024.10.31 |
스레드 _ JOIN (0) | 2024.10.29 |
스레드 정보와 생명주기 (0) | 2024.10.29 |