게시글
질문&답변
ExecutorServic 우아한 종료 isTerminated()에 대해서 질문
작성하신 문장 중 볼록 된 부분 입니다. 문장 바로 위에 있는 사진을 기준으로 설명드립니다.awaitTermination()은 블로킹 메서드입니다. 또한 해당 메서드는 '종료 기능'을 가진 메서드가 아니고, '종료될 때까지 기다리는' 메서드일 뿐입니다.shutdown(), shutdownNow() 등은 논블로킹 메서드입니다. 따라서 해당 메서드를 호출한 메서드는 그 직후에 본인의 코드들을 이어서 계속 수행합니다.line 3510초를 블로킹 대기했으나, 작업들이 아직 수행중이기에 false가 리턴됩니다. 따라서 line 37의 로그가 실행됩니다.line 38shutdownNow()를 호출합니다. 따라서 스레드 풀에서 수행중인 작업에 대해서는 인터럽트를 발생시키고, 대기중인 작업에 대해서는 컬렉션으로 리턴하는 방식으로 종료 작업을 진행합니다.이때 논블로킹 메서드이므로 요청 스레드인 main 스레드는 곧바로 본인의 다음 코드인 line 44를 실행합니다.line 44스레드풀은 위에서 언급한 종료 작업을 아직 진행중입니다. 즉 아직 종료된 상태가 아니고, 종료를 위해 준비하고 있는 상태입니다. 따라서 es.isTerminated()는 false를 리턴합니다.line 48다시 10초를 블로킹 대기합니다. 기다리는 10초 동안 종료 작업은 잘 마무리되었습니다.따라서 line 53의 es.isTerminated()는 true를 리턴합니다.
- 0
- 2
- 71
질문&답변
future.get()
[스레드풀의 스레드가 작업을 시작하는 시점]은 [get()을 호출하는 시점]이 아니고, [submit()을 호출하는 시점]입니다. 또한 submit()은 non-blocking method이므로 main 스레드는 스레드 풀에게 작업을 수행해달라는 명령만 전달하고, 본인의 스택에 있는 코드를 이어서 수행할 수 있습니다.Future future1 = es.submit(task1); // pool-1-thread-1 작업 시작. non-blocking Future future2 = es.submit(task2); // pool-1-thread-2 작업 시작. non-blocking // non-blocking이므로 main에 이어지는 코드들이 계속 수행... // ... log(end - st + "ms"); // 마찬가지로 수행됨 Integer sum2 = future2.get(); // ...
- 0
- 2
- 189
질문&답변
단일 쓰레드에서 성능 차이가 발생하는 이유
먼저 질문 내용 중에서 BasicInteger와 VolatileInteger의 경우에는 캐시를 사용하는가, 메인 메모리를 사용하는가의 차이로 성능 문제가 발생했다는 것은 알겠는데 ...라고 해 주셨으니 거기에 맞춰 설명드립니다.synchronized 키워드를 명시하여 사용하는 임계 구역은 '여러 스레드가 접근할 수 있다. 단, 락을 획득한 스레드만 (즉 동시에 최대 한 스레드만) 접근하여 연산을 수행할 수 있다'라는 설명도 이해가 가시나요?위 설명이 이해가 가신다는 전제 하에 마저 설명을 드리자면 여러 스레드가 접근할 수 있다 == 메인 메모리에 존재한다 입니다.여기까지 이해가 되셨다면, 결국 BasicInteger와 SyncInteger 간의 성능 차이도 캐시를 사용하는가, 메인 메모리를 사용하는가의 차이로 발생한다는 것도 아실 수 있습니다. (조금 비약이 있는 설명이지만 질문 내용에 답변을 드리기 위해서는 이 정도면 충분하다고 생각하여 다음과 같이 설명드리겠습니다. 더 궁금하시면 구글에 검색하시면 여러 내용들이 있어요.)정리하면 캐시 메모리는 각 스레드마다 가지고 있는 종속적인 공간입니다. Thread-1은 Thread-2의 캐시에 접근할 수 없습니다.그리고 메인 메모리는 아무 스레드나 접근 가능한 공간입니다. Thread-1과 Thread-2은 (권한이 있다는 가정하에) 동일한 메모리 영역에 접근할 수 있습니다.따라서 메인 메모리 상에 존재하는 임계 영역에도 (락만 가지고 있다면) Thread-1과 Thread-2 둘 다 접근이 가능합니다.
- 0
- 2
- 106
질문&답변
문제와 풀이2번 궁금증
t1.join()은 main()에서 호출했습니다.t1.join()은 t1의 run() 메서드에서 정의한 모든 코드가 수행될 때까지 t1.join() 다음에 있는 코드를 수행하지 않도록 합니다.따라서 main()에서 t1.join() 이전에 있는 t1.start(), t2.start(), t3.start()는 t1의 모든 코드가 실행되었는지 여부에 구애받지 않고 모두 정상적으로 작동합니다.t1 스레드를 비롯하여 모든 t 스레드는 로그를 출력 후 1초간 대기합니다.1초라는 시간은 다른 t2, t3 스레드가 한 줄의 로그를 출력하는 데에는 충분한 시간입니다. 따라서 t1이 1초 대기하는 동안 t2, t3도 각자 로그를 출력하고 1초를 대기합니다. 그러면 또 t2, t3가 대기하는 시간동안 t1의 1초 대기가 끝나고 다음 로그를 출력하고, ...따라서 다음과 같은 출력 결과를 얻습니다.16:58:43.144 [ t1] 1 16:58:43.144 [ t2] 1 16:58:43.144 [ t3] 1 16:58:44.152 [ t1] 2 16:58:44.152 [ t2] 2 16:58:44.152 [ t3] 2 16:58:45.154 [ t1] 3 16:58:45.154 [ t2] 3 16:58:45.154 [ t3] 3 모든 스레드 실행 완료. 수행 시간: 3초 Process finished with exit code 0
- 1
- 2
- 71
질문&답변
생산자 소비자 대기 공간 분리 예제5 분석 질문
AI 인턴은 signal()을 받을 생산자가 없다면 신호가 대기열로 보내진다고 했는데, 이는 잘못된 답변같아서 적습니다.혹시 다른 의견들 있으시면 답글 바랍니다.소비자가 producerCond에 signal()을 보낼 때 producerCond에 생산자가 대기하고 있고, 락을 양보받는다.를 보장할 수 없습니다. producerCond.signal()을 호출하려면 producerCond.await()가 호출된 적이 있어야 한다 등의 조건을 사용하여 이를 보장하도록 할 수는 있겠으나, (저도 아직 뒷부분을 학습하지는 않아서) 현재까지 배운 내용으로는 보장할 수 없다고 생각하는 것이 맞습니다.producerCond에 대기하고 있던 생산자가 없을 때 소비자가 호출한 producerCond.signal()은 어떻게 되는가?-> signal()을 호출한 것이 무시되고 사라진다.consumerCond에 있는 소비자들은 계속 대기한다.이후에 언젠가 producer가 데이터를 생산하고, consumerCond.signal()을 호출해주기 전까지 소비자들의 대기는 계속된다. 2번의 경우 다음과 같은 경우를 생각해보면 유사한 메커니즘입니다.public static int getNumber1() { return 1; } // 리턴(신호)을 받아줄 곳이 없으므로 리턴값인 1은 무시되고, 사라집니다. 이후에 이 값을 다시 사용할 수 없습니다. getNumber1() // 리턴 값을 받아서 보관할 곳이 있으므로 무시되지 않습니다. int num = getNumber(); System.out.println(num); // prints 1
- 0
- 3
- 48
질문&답변
break 부분이 왜 필요한지 헷갈립니다
while (!Thread.interrupted()) { if (jobQueue.isEmpty()) continue; try { String job = jobQueue.poll(); log("출력 시작: " + job + ", 대기 문서: " + jobQueue); Thread.sleep(3000); // 출력 작업에 3초가 걸린다고 가정 log("출력 완료: " + job); } catch (InterruptedException e) { log("인터럽트 발생. message=" + e.getMessage()); // break가 없다면, interrupt status=false로 바뀐 상태에서, // 다시 while condition으로 돌아가게 되는데, 그러면 while loop에 갇히게 된다. // 예외 발생은 try 블록을 벗어나서 catch 블록으로 가게끔 하는 것이지, // while loop을 벗어나게 해주는 것은 아니다. break; } } log("프린터 종료"); 실행 결과 - 위 코드에서 break 주석처리 했다고 가정16:40:45.356 [ main] 프린터할 문서를 입력하세요. 종료 (q): a 16:40:47.101 [ main] 프린터할 문서를 입력하세요. 종료 (q): 16:40:47.110 [ printer] 출력 시작: a, 대기 문서: [] b 16:40:47.293 [ main] 프린터할 문서를 입력하세요. 종료 (q): c 16:40:47.599 [ main] 프린터할 문서를 입력하세요. 종료 (q): d 16:40:47.853 [ main] 프린터할 문서를 입력하세요. 종료 (q): q # ---------------------- 작업 중지를 지시함. 16:40:48.136 [ printer] 인터럽트 발생. message=sleep interrupted # 만약 catch block에 break가 없다면? # 인터럽트는 발생하고, 이를 소비했는데 while loop을 빠져 나가지 못하는 문제가 발생! 16:40:48.137 [ printer] 출력 시작: b, 대기 문서: [c, d] 16:40:51.141 [ printer] 출력 완료: b 16:40:51.141 [ printer] 출력 시작: c, 대기 문서: [d] 16:40:54.142 [ printer] 출력 완료: c 16:40:54.143 [ printer] 출력 시작: d, 대기 문서: [] 16:40:57.145 [ printer] 출력 완료: d Process finished with exit code 130 (interrupted by signal 2:SIGINT)
- 0
- 3
- 155
질문&답변
정적내부클래스 new 로 생성에 관한 질문(인터럽트-시작1강 중...)
질문자님이 생각하신 것은 static 키워드를 사용하지 않은 일반적인 Inner class에 해당하는 규칙입니다.다음과 같은 정적 중첩 클래스의 경우 OuterClass없이 NestedClass만 독립적으로 생성할 수 있으며, 따라서 다음 예시와 같이 중첩시키지 않고 두 클래스를 따로 만든 것과 동일합니다.따라서 OuterClass를 통해 접근하지 않고도 곧바로 new NestedClass()와 같이 인스턴스를 생성할 수 있습니다.// case 1. 파일1에 outer class와 static nested class public class OuterClass { ... static class NestedClass { ... } } // case 2. 파일 두개 각각에 outer class와 nested class // 이 경우 서로 다른 파일로 각 클래스를 유지하기에 outer와 nested 개념은 아니지만, case 1과의 비교를 쉽게 하기 위해 클래스 명을 그대로 사용. // > public class OuterClass { ... } // > public class NestedClass { ... } 다음과 같이 static 키워드를 사용하지 않은 InnerClass의 경우 질문자님께서 생각하신 부분처럼 동작합니다.public class InnerOuter { ... class InnerClass { ... } }public class InnerOuterMain public static void main(String[] args) { InnerOuter outer = new InnerOuter(); // 이 경우, outer 클래스의 인스턴스를 inner 클래스 인스턴스를 생성 InnerOuter.InnerClass inner = outer.new InnerClass();
- 0
- 2
- 46
질문&답변
JoinMainV0에 관한 질문
멀티스레드는 작업의 순서를 보장하지 않는다.lgh8079님과 같이 출력된 이유를 굳이 생각해보자면 다음과 같은 시나리오로 유추할 수 있습니다.thread1.start()를 호출 -> 작업1) 객체로만 존재하던 thread1에게 실제로 물리적인 스택 공간을 할당하는 작업, 대기 큐에 넣는 작업 등을 수행. -> 작업2) 위 작업 등을 모두 수행하면 그제서야 run() 메서드를 호출 => "작업 시작"이 출력 따라서 thread1.start()를 호출함으로 인해 작업1)이 수행되는 동안에 main 스레드는 다음 스택 프레임인 log("End")를 수행하고/완료합니다. // ㄴ 콘솔에 [ main] End 출력 그 뒤에야 thread1의 작업1)이 완료되고, thread의 작업2)를 수행하게 됩니다. // ㄴ 콘솔에 [ thread-1] 작업 시작 출력다시 정리해서 말하자면, thread1.start()를 호출한다고 해서 thread1이 곧바로 run()을 호출하는 것이 아니고, thread1에 물리적 스택 메모리를 할당하는 등의 사전 작업이 선행되어야 하고 그 동안 main 스레드는 이와 무관하게 본인의 할 일(log("End") 호출)을 계속 수행하기 때문에 화면과 같이 출력된 것이라고 유추할 수 있습니다.
- 0
- 2
- 96
질문&답변
run() 메서드가 종료되면 Thread-0을 회수하나요?
자문자답: 맞습니다 ~
- 0
- 2
- 65
질문&답변
스케줄링에 대한 질문입니다.
1개의 CPU 코어는 한 번에 하나의 스레드만 실행할 수 있습니다. 다만 시분할을 통해 마치 여러 개의 스레드가 동시에 처리되는 것처럼 느낄 정도로 여러 스레드가 빠르게/조금씩/번갈아가며 처리됩니다.프로세스 스케쥴링과 스레드 스케쥴링은 다릅니다.두 개의 프로세스를 가정하겠습니다. 프로세스1=운영체제(윈도우), 프로세스2=인터넷브라우저(유튜브)프로세스끼리 우선순위를 비교한다면 당연하게도 컴퓨터 실행의 기본이 되는 운영체제 프로세스가 우선적인 순위를 가집니다. 운영체제가 제대로 실행되지 않고 있다면 인터넷 브라우저는 당연히 실행되지 못하기 때문에 우선적으로 운영체제의 제대로 된 실행을 보장해 주어야 합니다.운영체제 내부에서 본다면 마우스 클릭, 키보드 클릭 등의 이벤트 처리, 화면 업데이트, 버전 업데이트 점검 등의 여러 스레드가 수행될 것입니다. 이 중에서 사용자와 즉각적으로 소통해야 하는 마우스/키보드 처리나 이에 따른 화면 업데이트 등의 스레드가 더 높은 우선 순위를 가질 것입니다. 버전 업데이트 점검등은 보통 사용자가 작업중일 확률이 적은 심야 시간대나, 사용량이 적을 때(유튜브 등의 작업을 하고 있지 않을 때) 수행할 것입니다.일상생활에 비유를 하자면 [옷을 입는다]와 [차를 타러간다] 두 프로세스에 대해서 당연히 [옷을 입는다]가 우선순위를 가져야 합니다.[옷을 입는다] 프로세스의 내부 스레드를 보자면 {속옷을 입는다}, {바지를 입는다}, {외투를 입는다} 등의 여러 스레드가 있는데 이 중에서는 {속옷을 입는다}스레드가 더 높은 우선순위를 가져야 합니다. {바지를 입는다}를 먼저 수행하게 되면 우스꽝스럽게 외출하게 되겠죠.
- 0
- 2
- 76