<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>study</title>
    <link>https://handr95.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sat, 11 Apr 2026 10:08:55 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>handr95</managingEditor>
    <item>
      <title>[Java] Integer.parseInt()와 Integer.valueOf()의 차이</title>
      <link>https://handr95.tistory.com/46</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Integer.parseInt()&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. parseInt(String s)&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전달 받은 문자열을 10진수 int로 파싱합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Parameters:&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;s - 파싱할 문자열&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Returns:&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전달 받은 문자열을 10진수로 표현한 int 값.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Throws:&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/NumberFormatException.html&quot;&gt;NumberFormatException&lt;/a&gt; - String에 파싱 가능한 int가 없는 경우.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1732537480681&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Test
void parseInt() {
 &amp;nbsp; &amp;nbsp;int value1 = Integer.parseInt(&quot;123&quot;);
 &amp;nbsp; &amp;nbsp;System.out.println(value1); &amp;nbsp; &amp;nbsp; // 123
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. parseInt(String s,int radix)&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전달받은 문자열 s를, 두번째 인수에 지정된 기수(radix)를 사용하여 int로 파싱합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 문자열 &quot;101&quot;을 2진수(radix 2)로 해석하면 십진수 값 5로 변환됩니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Parameters:&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;s - 파싱할 문자열&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;radix - s를 파싱하는 동안 사용할 기수&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Returns:&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지정된 기수(radix)에서 문자열 인수로 표현한 정수&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Throws:&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/NumberFormatException.html&quot;&gt;NumberFormatException&lt;/a&gt; - String에 파싱 가능한 int가 없는 경우.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;NumberFormatException 예외가 발생될 수 있는 상황&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인수 s의 값이 null이거나 길이가 0 일 경우&lt;/li&gt;
&lt;li&gt;진수가 Character.MIN_RADIX(=2)보다 작거나 Character.MAX_RADIX(=36)보다 클 경우&lt;/li&gt;
&lt;li&gt;인수 s의 모든 문자들이 지정된 진수에서 사용 가능한 숫자가 아닌 경우
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Character.digit(char, int) 메서드가 &lt;b&gt;0 이상의 값을 반환하는 경우&lt;/b&gt;, 해당 문자는 지정된 진수의 유효한 숫자로 간주&lt;/li&gt;
&lt;li&gt;단, 문자열이 길이가 1보다 긴 경우 첫 번째 문자는 빼기 기호 '-'('\u002D') 또는 더하기 기호 '+'('\u002B')가 될 수 있습니다.&lt;/li&gt;
&lt;li&gt;예를 들어, 16진수(기수 16)를 사용한다면, 사용 가능한 숫자는 0부터 9, 그리고 A부터 F까지 입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;인수 s의 값이 int 유형이 아닌 경우&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1732537505988&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Test
void parseInt() {
 &amp;nbsp; &amp;nbsp;int value1 = Integer.parseInt(&quot;101&quot;, 2);
 &amp;nbsp; &amp;nbsp;int value2 = Integer.parseInt(&quot;A3&quot;, 16);
 &amp;nbsp; &amp;nbsp;int value3 = Integer.parseInt(&quot;-10&quot;, 10);
 &amp;nbsp; &amp;nbsp;System.out.println(value1); &amp;nbsp; &amp;nbsp; // 5
 &amp;nbsp; &amp;nbsp;System.out.println(value2); &amp;nbsp; &amp;nbsp; // 163
 &amp;nbsp; &amp;nbsp;System.out.println(value3); &amp;nbsp; &amp;nbsp; // -10
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. parseInt(CharSequence s,int beginIndex,int endIndex,int radix)&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전달받은 CharSequence를 지정된 범위 내에서 지정된 기수(radix)를 사용하여 int로 파싱&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;beginIndex와 endIndex로 변환할 범위를 지정&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Parameters&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;s: 파싱할 문자 시퀀스(CharSequence). String, StringBuilder, StringBuffer 등과 같은 CharSequence를 상속 받은 구현체 들이 들어 갈 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;beginIndex: 파싱을 시작할 위치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;endIndex: 파싱을 끝낼 위치의 바로 다음 인덱스. (endIndex - 1까지 파싱된)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;radix: s를 파싱하는 동안 사용할 기수&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Returns&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지정된 범위에서 &lt;b&gt;주어진 기수로 표현된 정수 값&lt;/b&gt;을 반환합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Throws&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NullPointerException - s가 null인 경우.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IndexOutOfBoundsException - beginIndex가 음수이거나 beginIndex가 endIndex보다 크거나 endIndex가 s.length()보다 큰 경우.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NumberFormatException&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주어진 범위 내의 &lt;b&gt;문자열이 유효한 정수를 나타내지 않는 경우&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;radix가 2보다 작거나 36보다 큰 경우&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;beginIndex와 endIndex가 유효한 범위가 아닌 경우&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주의사항&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 메서드는 파싱 중에 &lt;b&gt;CharSequence가 변경될 위험을 방지하지 않습니다&lt;/b&gt;. 즉, 파싱을 하는 동안 원래의 CharSequence가 변경되면 예기치 않은 결과가 발생할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Test
void parseInt() {
 &amp;nbsp; &amp;nbsp;// 인덱스 1부터 4까지의 범위(`&quot;234&quot;` 부분)를 10진수로 변환
 &amp;nbsp; &amp;nbsp;int value = Integer.parseInt(&quot;12345&quot;, 1, 4, 10);
 &amp;nbsp; &amp;nbsp;System.out.println(value); &amp;nbsp; &amp;nbsp; &amp;nbsp;//234
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Integer.valueOf()&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. valueOf(String s)&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전달 받은 문자열을 10진수 Integer 객체로 파싱합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;parseInt(String s, int radix) 메서드를 이용해 문자열을 파싱한 후, 그 결과를 &lt;b&gt;Integer 객체&lt;/b&gt;로 감싸서 반환하는 방식입니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public static Integer valueOf(String s) throws NumberFormatException {
 &amp;nbsp; &amp;nbsp;return Integer.valueOf(parseInt(s, 10));
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Parameters&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;s&lt;/b&gt;: &lt;b&gt;파싱할 문자열&lt;/b&gt;로, 십진수 정수 표현을 포함하고 있어야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Returns&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전달 받은 문자열을 10진수로 표현한 Integer 객체&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Throws&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;NumberFormatException&lt;/b&gt; - 문자열 s가 &lt;b&gt;파싱 가능한 정수 값을 포함하지 않는 경우&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;@Test void valueOf() { &amp;nbsp; &amp;nbsp;Integer value1 = Integer.valueOf(&quot;123&quot;); &amp;nbsp; &amp;nbsp;Integer value2 = Integer.valueOf(&quot;-45&quot;); &amp;nbsp; &amp;nbsp;System.out.println(value1); &amp;nbsp; &amp;nbsp; // 123 &amp;nbsp; &amp;nbsp;System.out.println(value2); &amp;nbsp; &amp;nbsp; // -45 }&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. valueOf(String s,int radix)&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전달받은 문자열 s를, 두번째 인수에 지정된 기수(radix)를 사용하여 Integer 객체로 파싱합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;parseInt(String s, int radix) 메서드를 이용해 문자열을 파싱한 후, 그 결과를 &lt;b&gt;Integer 객체&lt;/b&gt;로 감싸서 반환하는 방식입니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public static Integer valueOf(String s, int radix) throws NumberFormatException {
 &amp;nbsp; &amp;nbsp;return Integer.valueOf(parseInt(s,radix));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Parameters&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;s&lt;/b&gt; - &lt;b&gt;파싱할 문자열&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;radix&lt;/b&gt; - s를 파싱하는 동안 사용할 기수&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Returns&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;**지정된 기수(radix)**에서 문자열 인수로 표현한 Integer 객체&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Throws&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NumberFormatException&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;문자열 s가 파싱 가능한 정수 값을 포함하지 않는 경우&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;radix가 2보다 작거나 36보다 큰 경우&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Test
void valueOf() {
 &amp;nbsp; &amp;nbsp;Integer value1 = Integer.valueOf(&quot;101&quot;, 2);
 &amp;nbsp; &amp;nbsp;Integer value2 = Integer.valueOf(&quot;FF&quot;, 16);

 &amp;nbsp; &amp;nbsp;System.out.println(value1); &amp;nbsp; &amp;nbsp; // 5
 &amp;nbsp; &amp;nbsp;System.out.println(value2); &amp;nbsp; &amp;nbsp; // 255
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. valueOf(int i)&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전달받은 CharSequence를 지정된 범위 내에서 지정된 기수(radix)를 사용하여 Integer 객체로 파싱&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;valueOf(int i) 메서드&lt;/b&gt;는 주어진 정수 값 i를 &lt;b&gt;Integer 객체로 감싸서 반환&lt;/b&gt;합니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public static Integer valueOf(int i) {
 &amp;nbsp; &amp;nbsp;if (i &amp;gt;= IntegerCache.low &amp;amp;&amp;amp; i &amp;lt;= IntegerCache.high)
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;return IntegerCache.cache[i + (-IntegerCache.low)];
 &amp;nbsp; &amp;nbsp;return new Integer(i);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 메서드는 &lt;b&gt;자주 요청되는 값들을 캐싱하여 성능을 개선&lt;/b&gt;합니다. &lt;b&gt;캐싱되는 값의 범위는 -128에서 127까지&lt;/b&gt;로, 이 범위 내의 값들은 &lt;b&gt;항상 캐싱되어 재사용&lt;/b&gt;됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;128에서 127 사이의 값은 미리 생성된 Integer 객체가 캐싱&lt;/b&gt;되기 때문에, 동일한 값에 대해 &lt;b&gt;항상 동일한 객체&lt;/b&gt;를 반환합니다. 이 외의 범위의 값들도 &lt;b&gt;캐싱될 수는 있지만&lt;/b&gt;(ex. JVM 옵션 : -XX:AutoBoxCacheMax=200), 캐싱이 보장되지는 않습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Parameters&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;i&lt;/b&gt;: Integer 객체로 &lt;b&gt;변환할 int 값&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Returns&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 int 값 i를 나타내는 &lt;b&gt;Integer 객체&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Test
void valueOf() {
 &amp;nbsp; &amp;nbsp;Integer value1 = Integer.valueOf(42);
 &amp;nbsp; &amp;nbsp;System.out.println(value1); // 42

 &amp;nbsp; &amp;nbsp;// -128에서 127 사이의 범위에서는 동일한 객체를 참조
 &amp;nbsp; &amp;nbsp;Integer a = Integer.valueOf(127);
 &amp;nbsp; &amp;nbsp;Integer b = Integer.valueOf(127);
 &amp;nbsp; &amp;nbsp;System.out.println(a == b); // true

 &amp;nbsp; &amp;nbsp;Integer c = Integer.valueOf(128);
 &amp;nbsp; &amp;nbsp;Integer d = Integer.valueOf(128);
 &amp;nbsp; &amp;nbsp;System.out.println(c == d); // false
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;parseInt와 valueOf의 차이&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 반환 타입&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Integer.parseInt()는 기본 자료형 int를 반환합니다.&lt;/li&gt;
&lt;li&gt;Integer.valueOf()는 Integer 객체를 반환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 캐싱&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Integer.valueOf()는 -128에서 127 범위의 값을 캐싱하여 성능을 향상시킵니다.&lt;/li&gt;
&lt;li&gt;Integer.parseInt()는 캐싱을 사용하지 않고 항상 새로운 기본형 int 값을 반환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 사용 목적&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Integer.parseInt()는 원시(primitive) 타입의 int 값을 필요로 할 때 사용됩니다.&lt;/li&gt;
&lt;li&gt;Integer.valueOf()는 Integer 객체를 필요로 할 때 사용됩니다. 예를 들어, 컬렉션에 정수를 저장하거나 객체와의 비교가 필요할 때 유용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ChatGpt - &lt;a href=&quot;https://chatgpt.com/&quot;&gt;https://chatgpt.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Integer.html#valueOf(java.lang.String)&quot;&gt;https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Integer.html#valueOf(java.lang.String)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Integer.html#parseInt(java.lang.String)&quot;&gt;https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Integer.html#parseInt(java.lang.String)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>[JAVA]</category>
      <category>java</category>
      <category>PARSEINT</category>
      <category>valueOf</category>
      <author>handr95</author>
      <guid isPermaLink="true">https://handr95.tistory.com/46</guid>
      <comments>https://handr95.tistory.com/46#entry46comment</comments>
      <pubDate>Mon, 25 Nov 2024 21:25:40 +0900</pubDate>
    </item>
    <item>
      <title>[Java] codePoint 메서드</title>
      <link>https://handr95.tistory.com/45</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. codePointCount(int beginIndex, int endIndex)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;beginIndex : 범위 시작 인덱스&lt;/li&gt;
&lt;li&gt;endIndex : 범위 종료 인덱스&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 내에서 지정된 범위 안에 있는 유니코드 코드 포인트 개수를 반환합니다. 해당 메서드는 문자(char)가 아닌 코드 포인트(code point)를 기준으로 합니다. 서로게이트 페어(surrogate pair)로 표현되는 유니코드 문자를 정확하게 세는 데 유용합니다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;@Test
public void codePointCount_test() {
    String ex = &quot; Hello&quot;;   // ' '는 서로게이트 페어로 이루어진 하나의 유니코드 문자
    System.out.println(ex.codePointCount(0, ex.length()));  //6
    System.out.println(ex.length());  //7
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;※서로게이트 페어(surrogate pair)란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로게이트 페어는 유니코드에서 하나의 문자를 표현하기 위해 두 개의 16비트 char를 사용하는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java의 경우 내부적으로 문자를 16비트 크기(unsinged, 0 ~ 65,535)의 char로 표현하지만, 유니코드의 모든 문자를 16비트로 표현하기에는 충분하지 않기 떄문에 이를 해결하기 위해 서로게이트 페어라는 개념이 도입되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니코드는 약 110만 개의 문자를 표현할 수 있는 범위를 가지고 있으며, 이 중 초기 65,536개(0x0000 ~ 0xFFFF)는 BMP(Basic Multilingual Plane, 기본 다국어 평면)라고 불리는 영역입니다. 이영역의 문자는 하나의 char로 표현할 수 있습니다. 하지만 나머지 유니코드 문자는 16비트로는 표현할 수 없기 때문에, 보충 평면의(Supplementary Planes)의 문자는 서로게이트 페어를 사용하여 두개의 char로 표현됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서로게이트 페어의 구조&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로게이트 페어는 두 개의 16비트 char로 구성되며, 첫 번째 char는 상위 서로게이트(high surrogate), 두 번째 char는 하위 서로게이트(low surrogate)로 불립니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상위 서로게이트 범위: 0xD800 ~ 0xDBFF(첫 6비트는 항상 고정되어 110110라는 값이 설정됨 - 상위/하위 구분 용도)&lt;/li&gt;
&lt;li&gt;하위 서로게이트 범위: 0xDC00 ~ 0xDFFF(첫 6비트는 항상 고정되어 110110라는 값이 설정됨 - 상위/하위 구분 용도)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 게이트 페어는 이 두 범위의 조합을 통해 유니코드의 보충 평면에 있는 문자를 표현합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lsquo; &amp;rsquo;문자의 경우 앞서 설명했던 BMP 범위를 벗어나는 문자 이므로, 두 개의 char로 표현됩니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;@Test
public void surrogatePair_test() {
    String heart = &quot; &quot;;
    System.out.println(heart.length()); //2
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우, &amp;lsquo; &amp;rsquo;는 두 개의 char로 표현되지만, 실제로는 하나의 유니코드 문자입니다. 이렇게 &lt;b&gt;하나의 유니코드 문자를 표현되기 위해 두 개의 16비트 char를 사용하는 경우 서로게이트 페어라고 말합니다.&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. codePoints()&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열에 포함된 유니코드 코드 포인트를 스트림(IntSream)으로 반환합니다.&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;@Test
public void codePoints_test(){
    String surrogate = &quot; Hello&quot;;
    /**
     * 128512
     * 72
     * 101
     * 108
     * 108
     * 111
     */
    surrogate.codePoints().forEach(System.out::println)
    /**
     * 55357
     * 56832
     * 72
     * 101
     * 108
     * 108
     * 111
     */
    surrogate.chars().forEach(System.out::println);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. codePointAt(int index)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 인덱스 위치의 유니코드 코드 포인트를 반환합니다. char 값이 아닌 코드 포인트를 반환하기 때문에, 서로게이트 페어 문자를 처리하기 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;@Test
public void codePointAt_test(){
    String str = &quot; Hello&quot;;
    int codePoint = str.codePointAt(0);
    System.out.println(codePoint);  // 128512 ( 의 코드 포인트)
    int index = str.charAt(0);
    System.out.println(index);  // 55357 ( 의 상위 서로게이트 값 반환)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. offsetByCodePoints(int index, int codePointOffset)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;index: 이동을 시작할 인덱스. 이 인덱스는 문자열에서 특정 char의 위치를 나타냅니다.&lt;/li&gt;
&lt;li&gt;codePointOffset: 이동할 코드 포인트의 개수. 이 값은 양수이면 앞으로, 음수이면 뒤로 이동합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 인덱스 에서 시작하여, 지정된 코드 포인트 수만큼 앞으로 또는 뒤로 이동한 위치를 반환합니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Test
public void offsetByCodePoints_test() {
    String str = &quot; Hello&quot;;
    int newIndex = str.offsetByCodePoints(0, 2);  // 첫 번째 문자에서 두 번째 코드 포인트로 이동
    System.out.println(newIndex);  // 3 ( 는 서로게이트 페어로 2개의 char를 차지)
    System.out.println(str.charAt(newIndex));   // e
    
    str = &quot;HHello&quot;;
    newIndex = str.offsetByCodePoints(0, 2); 
    System.out.println(newIndex);  // 2
    System.out.println(str.charAt(newIndex));   // e
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html#codePointAt(int)&quot;&gt;https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html#codePointAt(int)&lt;/a&gt;OpenAI의&lt;/li&gt;
&lt;li&gt;ChatGPT :&lt;a href=&quot;https://chatgpt.com/&quot;&gt;https://chatgpt.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>[JAVA]</category>
      <category>codepoint</category>
      <category>java</category>
      <author>handr95</author>
      <guid isPermaLink="true">https://handr95.tistory.com/45</guid>
      <comments>https://handr95.tistory.com/45#entry45comment</comments>
      <pubDate>Wed, 16 Oct 2024 22:06:00 +0900</pubDate>
    </item>
    <item>
      <title>[Jenkins] 파이프라인 confirm/prompt  확인창 추가</title>
      <link>https://handr95.tistory.com/44</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. confirm 확인창 추가&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;175&quot; data-origin-height=&quot;116&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yV7UC/btsH2W1KUQy/7bUYDT23cmw0fyLAPKHUlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yV7UC/btsH2W1KUQy/7bUYDT23cmw0fyLAPKHUlK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yV7UC/btsH2W1KUQy/7bUYDT23cmw0fyLAPKHUlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyV7UC%2FbtsH2W1KUQy%2F7bUYDT23cmw0fyLAPKHUlK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;175&quot; height=&quot;116&quot; data-origin-width=&quot;175&quot; data-origin-height=&quot;116&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;input message: 유저에게 보여줄 메시지, ok: '확인창에 보여줄 메시지(optional)'&lt;/p&gt;
&lt;pre class=&quot;puppet&quot;&gt;&lt;code&gt;pipeline {
    agent any

    stages {
        stage('Confirm Deployment') {
            steps {
                input message: '배포 하시겠습니까?', ok: 'Deploy'
            }
        }
        stage('Description') {
            steps {
                script{
                    currentBuild.description = &quot;description&quot;
                }
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;261&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bINqYE/btsH3yzktTS/8laf5r9NboN14Q2TzpKMgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bINqYE/btsH3yzktTS/8laf5r9NboN14Q2TzpKMgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bINqYE/btsH3yzktTS/8laf5r9NboN14Q2TzpKMgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbINqYE%2FbtsH3yzktTS%2F8laf5r9NboN14Q2TzpKMgK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;532&quot; height=&quot;261&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;261&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;513&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OaJwx/btsH1CXzaRu/nVdvUc4yotGKMF6GR68TJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OaJwx/btsH1CXzaRu/nVdvUc4yotGKMF6GR68TJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OaJwx/btsH1CXzaRu/nVdvUc4yotGKMF6GR68TJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOaJwx%2FbtsH1CXzaRu%2FnVdvUc4yotGKMF6GR68TJ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;513&quot; height=&quot;232&quot; data-origin-width=&quot;513&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;239&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfwDlZ/btsH09nWmix/3yMxtVhaZzeNSTJQKkkMZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfwDlZ/btsH09nWmix/3yMxtVhaZzeNSTJQKkkMZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfwDlZ/btsH09nWmix/3yMxtVhaZzeNSTJQKkkMZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfwDlZ%2FbtsH09nWmix%2F3yMxtVhaZzeNSTJQKkkMZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;531&quot; height=&quot;239&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;239&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 &amp;lsquo;Deploy' 버튼을 클릭하면 파이프라인의 다음 스텝으로 진행합니다. &amp;lsquo;Abort&amp;rsquo;나 &amp;lsquo;x&amp;rsquo; 버튼을 클릭하면 뒤에 있는 스텝들이 진행되지 않고 파이프라인은 종료됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. prompt 확인창 추가&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 4.png&quot; data-origin-width=&quot;223&quot; data-origin-height=&quot;193&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0DBBQ/btsH1NYZEUm/JSENSksPDA5gmvkrVIYxhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0DBBQ/btsH1NYZEUm/JSENSksPDA5gmvkrVIYxhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0DBBQ/btsH1NYZEUm/JSENSksPDA5gmvkrVIYxhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0DBBQ%2FbtsH1NYZEUm%2FJSENSksPDA5gmvkrVIYxhK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;223&quot; height=&quot;193&quot; data-filename=&quot;Untitled 4.png&quot; data-origin-width=&quot;223&quot; data-origin-height=&quot;193&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;parameter(optional)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빌드시 사용자에게 하나 이산의 매개변수 값을 지정하도록 요청함&lt;/li&gt;
&lt;li&gt;매개 변수가 하나만 지정되면 해당값이 입력 단계의 값이 된다.&lt;/li&gt;
&lt;li&gt;매개 변수를 여러개 지정한다면 매개 변수 이름으로 키가 지정된 맵이 된다.&lt;/li&gt;
&lt;li&gt;매개 변수를 지정하지 않은 경우 아무 값도 반환하지 않는다.&lt;/li&gt;
&lt;li&gt;사용 가능 타입 : string, text, booleanParam, choice, password&lt;/li&gt;
&lt;li&gt;defaultValue: 기본 입력값&lt;/li&gt;
&lt;li&gt;description: 파라미터 설명&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;position: absolute;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;pipeline {
    agent any

    stages {
        stage('Confirm Deployment') {
            steps {
                script {
                    def userInput = input(
                        message: '배포 하시겠습니까?:', 
                        parameters: [string(name: 'PASSPHRASE', defaultValue: '', description: '맞다면 YES 입력')]
                    )
                    // 사용자의 입력 정보를 가져옴. 'YES'를 입력하지 않은 경우 error 반환
                    if (userInput != 'YES') {
                        error &quot;Incorrect passphrase. Deployment aborted.&quot;
                    }
                }
            }
        }
        stage('Description') {
            steps {
                script{
                    currentBuild.description = &quot;description&quot;
                }
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 스크립트는 사용자가 'YES'를 입력하지 않으면 배포를 중단하고 파이프라인을 ABORTED 상태로 설정합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 5.png&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;337&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3ZZ4N/btsH15kVxih/bNa0pCBkXckKh14paZdN40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3ZZ4N/btsH15kVxih/bNa0pCBkXckKh14paZdN40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3ZZ4N/btsH15kVxih/bNa0pCBkXckKh14paZdN40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3ZZ4N%2FbtsH15kVxih%2FbNa0pCBkXckKh14paZdN40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;518&quot; height=&quot;337&quot; data-filename=&quot;Untitled 5.png&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;337&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 6.png&quot; data-origin-width=&quot;542&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pLLqi/btsH00xU8eq/bp3pRKKsHnmiN3TRX1nSx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pLLqi/btsH00xU8eq/bp3pRKKsHnmiN3TRX1nSx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pLLqi/btsH00xU8eq/bp3pRKKsHnmiN3TRX1nSx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpLLqi%2FbtsH00xU8eq%2Fbp3pRKKsHnmiN3TRX1nSx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;542&quot; height=&quot;250&quot; data-filename=&quot;Untitled 6.png&quot; data-origin-width=&quot;542&quot; data-origin-height=&quot;250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 7.png&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;335&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eqBmZl/btsH08vPT1t/SzpMNqd3eqWWM6Vz969wr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eqBmZl/btsH08vPT1t/SzpMNqd3eqWWM6Vz969wr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eqBmZl/btsH08vPT1t/SzpMNqd3eqWWM6Vz969wr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeqBmZl%2FbtsH08vPT1t%2FSzpMNqd3eqWWM6Vz969wr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;532&quot; height=&quot;335&quot; data-filename=&quot;Untitled 7.png&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;335&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 8.png&quot; data-origin-width=&quot;515&quot; data-origin-height=&quot;263&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ogyBg/btsH3xHcmcm/JFs27bPVY6FhEYwtqT0RG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ogyBg/btsH3xHcmcm/JFs27bPVY6FhEYwtqT0RG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ogyBg/btsH3xHcmcm/JFs27bPVY6FhEYwtqT0RG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FogyBg%2FbtsH3xHcmcm%2FJFs27bPVY6FhEYwtqT0RG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;515&quot; height=&quot;263&quot; data-filename=&quot;Untitled 8.png&quot; data-origin-width=&quot;515&quot; data-origin-height=&quot;263&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;파라미터를 여러개 사용하고 싶은 경우&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 9.png&quot; data-origin-width=&quot;258&quot; data-origin-height=&quot;294&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AnvjU/btsH2G5UNp6/bwKSDQyZh24OwvBj9PllDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AnvjU/btsH2G5UNp6/bwKSDQyZh24OwvBj9PllDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AnvjU/btsH2G5UNp6/bwKSDQyZh24OwvBj9PllDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAnvjU%2FbtsH2G5UNp6%2FbwKSDQyZh24OwvBj9PllDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;258&quot; height=&quot;294&quot; data-filename=&quot;Untitled 9.png&quot; data-origin-width=&quot;258&quot; data-origin-height=&quot;294&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;pipeline {
    agent any

    stages {
        stage('Confirm Deployment') {
            steps {
                script {
                    def userInput = input(
                        message: '배포 하시겠습니까?:', 
                        parameters: [
                            string(name: 'PASSPHRASE', defaultValue: '', description: '맞다면 YES 입력'),
                            booleanParam(name: 'FORCE_DEPLOY', defaultValue: false, description: '강제 배포 여부')
                            // 추가적으로 필요한 파라미터들을 여기에 추가함.
                        ]
                    )
                    // 사용자의 입력 정보를 가져옴. 파라미터가 여러개인 경우 파라미터 이름을 키로 가진 맵 형식으로 저장됨
                    // 'YES'를 입력하지 않은 경우 error 반환
                    if (userInput.PASSPHRASE != 'YES') {
                        error &quot;Incorrect passphrase. Deployment aborted.&quot;
                    }
                    // FORCE_DEPLOY 파라미터에 따라 강제 배포 여부를 처리할 수 있습니다.
                    if (userInput.FORCE_DEPLOY) {
                        // 강제 배포 처리 로직 추가
                    }
                }
            }
        }
        stage('Description') {
            steps {
                script {
                    currentBuild.description = &quot;description&quot;
                }
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.jenkins.io/doc/pipeline/steps/pipeline-input-step/&quot;&gt;https://www.jenkins.io/doc/pipeline/steps/pipeline-input-step/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.jenkins.io/doc/book/pipeline/syntax/#parameters&quot;&gt;https://www.jenkins.io/doc/book/pipeline/syntax/#parameters&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>[기타]</category>
      <category>confirm</category>
      <category>Jenkins</category>
      <category>prompt</category>
      <category>젠킨스</category>
      <category>확인창</category>
      <author>handr95</author>
      <guid isPermaLink="true">https://handr95.tistory.com/44</guid>
      <comments>https://handr95.tistory.com/44#entry44comment</comments>
      <pubDate>Mon, 17 Jun 2024 22:31:06 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 크리티컬 섹션(CRITICAL SECTION)</title>
      <link>https://handr95.tistory.com/43</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;크리티컬 섹션이란?&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;일반적인 프로그래밍 용어로서의 크리티컬 섹션&lt;/b&gt;: 다중 쓰레드 환경에서 여러 쓰레드가 동시에 접근할 수 있는 공유 자원을 사용하는 코드 영역을 지칭합니다. 크리티컬 섹션은 데이터의 일관성을 유지하기 위해 특정 시점에 한 쓰레드만 접근할 수 있어야 하는 부분입니다. 이러한 크리티컬 섹션을 보호하기 위해 뮤택스, 세마포어, 락 등의 동기화 메커니즘을 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Windows API에서의 크리티컬 섹션 객체&lt;/b&gt;: windows OS에서는 &quot;크리티컬 섹션&quot;이라는 특정 동기화 객체를 제공합니다. 이 객체는 windows 내에서 경량 동기화를 제공하며, 특히 같은 프로세스 내의 쓰레드들 사이의 동기화에 사용됩니다. windows의 크리티컬 섹션 객체는 뮤택스보다 빠르고 효율적이지만, 프로세스 간 동기화는 지원하지 않습니다. (Linux나 macOS 환경에서는 사용할 수 없습니다.)&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본 문서에서는 주로 &lt;code&gt;Windows API에서의 크리티컬 섹션 객체&lt;/code&gt;에 대해 설명합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;크리티컬 섹션 객체를 사용하는 이유&lt;/h2&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;thread&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;windows.h&amp;gt;

// 공유 자원
int counter = 0;

void IncrementCounter() {
    static std::random_device rd;
    static std::mt19937 gen(rd());
    static std::uniform_int_distribution&amp;lt;&amp;gt; dis(1, 100);
    std::this_thread::sleep_for(std::chrono::milliseconds(dis(gen))); // 랜덤 딜레이

    int localCounter = ++counter; // 증가된 값을 로컬 변수에 저장
    printf(&quot;Thread %d incremented counter to %d\n&quot;, std::this_thread::get_id(), localCounter);
}

int main() {
    std::vector&amp;lt;std::thread&amp;gt; threads;
    for (int i = 0; i &amp;lt; 100; ++i) {
        threads.push_back(std::thread(IncrementCounter));
    }

    for (auto&amp;amp; thread : threads) {
        thread.join();  // 모든 쓰레드가 종료될 때까지 대기
    }

    printf(&quot;Final counter value: %d\n&quot;, counter);

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공유 자원 인 &lt;code&gt;counter&lt;/code&gt;에 여러 쓰레드가 동시에 접근하여 카운트를 1씩 올려주는 코드를 작성했습니다. 실제 해당 코드를 실행한다면,&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;//...
Thread 51880 incremented counter to 95
Thread 40472 incremented counter to 96
Thread 21416 incremented counter to 97
Thread 21380 incremented counter to 98
Final counter value: 98&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 final 값이 100이 아닌 98이 나오는 것을 확인 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 이유는 여러 쓰레드가 동시에 &lt;code&gt;counter&lt;/code&gt; 변수를 증가시키려고 할 때 발생하는 &lt;b&gt;경쟁 조건(race condition)&lt;/b&gt; 때문입니다. 경쟁 조건은 두 개 이상의 쓰레드 또는 프로세스가 공유 자원(&lt;code&gt;counter&lt;/code&gt; 변수)에 대해 동시에 읽기 또는 쓰기 작업을 수행하려고 할 때 발생하며, 이 작업들의 실행 순서가 보장되지 않아 결과의 일관성(&lt;code&gt;counter&lt;/code&gt;의 결과가 98로 세팅)이 무시될 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;경쟁 조건의 발생 과정&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;동시성&lt;/b&gt;: 쓰레드는 동시에 &lt;code&gt;IncrementCounter&lt;/code&gt; 함수를 실행하고, 각각이 &lt;code&gt;counter&lt;/code&gt; 값을 증가시키려고 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;읽기-수정-쓰기 작업&lt;/b&gt;: &lt;code&gt;counter&lt;/code&gt; 변수의 증가 연산(&lt;code&gt;++counter&lt;/code&gt;)은 세 개의 다른 작업으로 분해될 수 있습니다:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;읽기&lt;/b&gt;: 현재 &lt;code&gt;counter&lt;/code&gt;의 값을 읽습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;수정&lt;/b&gt;: 읽은 값에 1을 더합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;쓰기&lt;/b&gt;: 수정된 값을 다시 &lt;code&gt;counter&lt;/code&gt;에 저장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;중첩 실행&lt;/b&gt;: 이 작업들 사이에 다른 쓰레드의 동일한 작업이 중첩될 수 있습니다. 예를 들어, 두 쓰레드가 거의 동시에 &lt;code&gt;counter&lt;/code&gt;의 값을 읽을 때, 두 쓰레드 모두 같은 값을 읽을 수 있습니다. 이후 각 쓰레드는 값을 &lt;code&gt;1&lt;/code&gt; 증가시키고 저장합니다. 이런 경우 실제로는 &lt;code&gt;counter&lt;/code&gt;가 두 번 증가해야 하지만, 실제로는 한 번만 증가하게 됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;이런 문제를 방지하기 위해서 크리티컬 섹션, 뮤택스, 세마포어 등의 동기화 기술을 사용합니다.&lt;/i&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;크리티컬 섹션 객체의 사용 방법&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;초기화 및 삭제&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;InitializeCriticalSection&lt;/code&gt; 함수를 사용하여 크리티컬 섹션 객체를 초기화합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DeleteCriticalSection&lt;/code&gt; 함수로 크리티컬 섹션 객체를 삭제하고 관련 리소스를 해제합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;진입 및 해제&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;EnterCriticalSection&lt;/code&gt; 함수를 사용하여 크리티컬 섹션에 진입합니다. 이 함수를 호출할 때, 해당 크리티컬 섹션을 이미 소유하고 있는 쓰레드가 있으면 호출한 쓰레드는 대기 상태로 들어갑니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LeaveCriticalSection&lt;/code&gt; 함수로 크리티컬 섹션을 해제합니다. 이 함수 호출로 해당 섹션을 대기하고 있던 다른 쓰레드가 진입할 수 있게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용 예시&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;thread&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;windows.h&amp;gt;

CRITICAL_SECTION cs; // 크리티컬 섹션 객체

void accessSharedResource(int threadNum) {
    EnterCriticalSection(&amp;amp;cs); // 크리티컬 섹션에 진입
    // 공유 자원 접근 코드
    std::cout &amp;lt;&amp;lt; &quot;Thread &quot; &amp;lt;&amp;lt; threadNum &amp;lt;&amp;lt; &quot; is accessing shared resource.&quot; &amp;lt;&amp;lt; std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1)); // 자원 사용 시뮬레이션
    std::cout &amp;lt;&amp;lt; &quot;Thread &quot; &amp;lt;&amp;lt; threadNum &amp;lt;&amp;lt; &quot; has finished accessing shared resource.&quot; &amp;lt;&amp;lt; std::endl;
    LeaveCriticalSection(&amp;amp;cs); // 크리티컬 섹션을 떠남
}

void threadFunction(int threadNum) {
    accessSharedResource(threadNum);
}

int main() {
    InitializeCriticalSection(&amp;amp;cs); // 크리티컬 섹션 초기화

    std::vector&amp;lt;std::thread&amp;gt; threads;
    for (int i = 0; i &amp;lt; 5; ++i) {
        threads.emplace_back(threadFunction, i);
    }

    for (auto&amp;amp; thread : threads) {
        thread.join(); // 각 쓰레드의 종료를 기다림
    }

    DeleteCriticalSection(&amp;amp;cs); // 크리티컬 섹션 삭제

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결과 화면&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Thread 3 is accessing shared resource.
Thread 3 has finished accessing shared resource.
Thread 0 is accessing shared resource.
Thread 0 has finished accessing shared resource.
Thread 1 is accessing shared resource.
Thread 1 has finished accessing shared resource.
Thread 2 is accessing shared resource.
Thread 2 has finished accessing shared resource.
Thread 4 is accessing shared resource.
Thread 4 has finished accessing shared resource.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;EnterCriticalSection을 사용하지 않았을 경우 출력 화면&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Thread Thread 0 is accessing shared resource.Thread 4 is accessing shared resource.

3 is accessing shared resource.
Thread 1 is accessing shared resource.
Thread 2 is accessing shared resource.
Thread 0 has finished accessing shared resource.Thread 1 has finished accessing shared resource.

Thread 4 has finished accessing shared resource.
Thread 3 has finished accessing shared resource.Thread 2 has finished accessing shared resource.&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 CRITICAL_SECTION을 사용할 때 RAII 패턴을 활용하여 사용하는 경우가 일반적이므로 RAII에 대한 설명을 아래에 추가로 작성합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;RAII(Resource Acquisition Is Initialization)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAII(Resource Acquisition Is Initialization) 패턴은 C++ 창시자인 비야네 스트롭스트룹이 제안한 자원 관리 방법입니다. 많은 현대 프로그래밍 언어들, 예를 들어 Java는 &lt;i&gt;가비지 컬렉션(Garbage Collection, GC)이 내장&lt;/i&gt;되어 있어 프로그램에서 더 이상 사용되지 않는 리소스를 자동으로 해제해줍니다. 이로 인해 개발자들은 자원 해제에 신경 쓸 필요가 적어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 C++은 다릅니다. C++에서는 한 번 획득한 자원을 프로그래머가 직접 해제해주지 않으면 프로그램이 종료될 때까지 자원이 계속 유지됩니다. 프로그램이 종료되면 운영 체제가 자원을 해제해주긴 하지만, 프로그램 실행 중에 발생한 메모리 누수는 버그의 일반적인 원인이 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++에서는 이 문제를 해결하기 위해 RAII 패턴을 사용합니다. 이 패턴은 리소스 관리를 스택에 할당된 객체를 통해 수행합니다. 객체가 생성될 때 필요한 리소스를 획득하고, 객체의 수명이 끝날 때 소멸자를 통해 리소스를 자동으로 해제합니다. 만약 리소스가 너무 크거나 스택에 적합하지 않은 경우, 해당 리소스는 객체에 의해 '소유'되며, 이 객체는 스택에 선언됩니다. 이처럼 객체가 리소스를 '소유한다'는 원칙이 바로 &quot;리소스 획득은 초기화&quot; 또는 RAII라고 불립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법을 통해, C++ 개발자들은 메모리 관리를 더 안전하고 효율적으로 수행할 수 있으며, 자원 해제를 잊어버리는 실수를 방지할 수 있습니다. RAII는 또한 예외 발생 시에도 자원이 안전하게 정리될 수 있도록 해, 프로그램의 안정성을 높이는 데 기여합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;핵심 개념&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;자원의 획득은 객체의 초기화와 함께 수행됩니다&lt;/b&gt;: 생성자에서 자원을 획득함으로써, 객체의 초기화 코드와 자원 획득 코드를 동일하게 관리할 수 있습니다. 이로 인해 자원이 성공적으로 획득되었는지 쉽게 확인할 수 있으며, 객체 사용이 가능한 상태에서 항상 자원이 유효하다는 것을 보장합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자원의 해제는 객체의 소멸과 함께 자동으로 수행됩니다&lt;/b&gt;: 소멸자에서 자원을 해제함으로써, 객체가 스코프를 벗어날 때 자동적으로 자원이 반환됩니다. 이는 메모리 누수를 방지하고, 예외가 발생해도 안정적으로 자원을 정리할 수 있도록 도와줍니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시 코드&lt;/h3&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;#include &amp;lt;cstdio&amp;gt;

class File final {
public:
    explicit File(std::FILE* file) : m_file{ file } {}
    ~File() { reset(); }

    // 복사 생성자와 복제 대입을 허용하지 않는다.
    File(const File&amp;amp; src) = delete;
    File&amp;amp; operator=(const File&amp;amp; rhs) = delete;

    // 이동 생성자의 이동 대입을 허용한다.
    File(File&amp;amp;&amp;amp; src) noexcept = default;
    File&amp;amp; operator=(File&amp;amp; rhs) noexcept = default;

    // get(), release(), reset()
    std::FILE* get() const noexcept { return m_file; }

    [[nodiscard]] std::FILE* release() noexcept {
        std::FILE* file{ m_file };
        m_file = nullptr;
        return file;
    }

    void reset(std::FILE* file = nullptr) noexcept {
        if (m_file) { fclose(m_file); }
    }
private:
    std::FILE* m_file{ nullptr };
};

int main() {
    try {
        // 파일 핸들러 객체가 스코프를 벗어나면서 파일이 자동으로 닫힘
        File myFile {fopen(&quot;input.txt&quot;, &quot;r&quot;)};
    } catch (const std::exception&amp;amp; e) {
        std::cerr &amp;lt;&amp;lt; &quot;An error occurred: &quot; &amp;lt;&amp;lt; e.what() &amp;lt;&amp;lt; std::endl;
    }
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;myFile 인스턴스가 스코프를 벗어나면 즉시 소멸자가 호출되면서 해당 파일이 자동으로 닫힙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAII 클래스를 사용할 때 흔히 저지르는 실수가 있는데 잘 알아 둘 필요가 있습니다. 특정한 스코프 안에서 RAII 인스턴스를 제대로 생성했다고 생각하는 코드 행을 실수로 작성하여, 실제로는 임시 객체를 생성해서 그 줄이 끝나면 곧바로 제거되는 경우가 있습니다. 이 문제는 표준 라이브러리 RAII 클래스인 std::unuque_lock을 사용해보면 확실히 드러납니다. unique_lock의 정상적인 실행 방법은 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;mutex&amp;gt;

class Foo {
public:
    void setData() {
        std::unique_lock&amp;lt;std::mutex&amp;gt; lock(m_mutex); // 이름이 있는 RAII 객체
        // lock 객체의 스코프가 끝나면 mutex는 자동으로 해제된다.
    }
private:
    std::mutex m_mutex;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 setData() 메서드는 RAII인 unique_lock의 객체를 이용하여 m_mutex 데이터 멤버에 락을 걸었다가 메서드가 끝날 즈음 자동으로 락을 해제하는 로컬 lock 객체를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이 lock 객체를 정의한 뒤 직접 사용하지 않기 때문에 다음과 같은 실수를 저지르기 쉽습니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;unique_lock&amp;lt;mutex&amp;gt;(m_mutex);    // 이름이 없는 RAII 객체, 이 줄을 실행하고 난 직후 바로 m_mutex가 해제된다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;unique_lock에 이름을 짓지 않았습니다. 이렇게 해도 컴파일은 되지만 의도와 다르게 실행됩니다. 이렇게 하면 m_mutex란 로컬 변수를 선언하고 unique_lock의 디폴트 생성자를 호출해서 이 변수를 초기화합니다. 따라서 데이터 멤버인 m_mutex에 대해 락이 걸리지 않습니다. 이에 대해 컴파일러가 경고 메시지를 출력하지만 경고 수준을 충분히 높여야 볼 수 있습니다. 예를 들면 아래와 같습니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;waring c4458: declaration if 'm_mutex' hides class member&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 균일 초기화 구문을 적용하면 컴파일러는 경고 메시지를 출력하지 않을 뿐만 아니라 의도와 다르게 작동합니다. 해당 코드도 마찬가지로 해당 줄을 실행하고 난 직후 m_mutex가 해제 됩니다.&lt;/p&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;unique_lock&amp;lt;mutex&amp;gt; {m_mutex};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 이름을 짓지도 않고 인수 이름도 잘못 적은 코드입니다. 이에 대해 컴파일러는 m이 참조되지 않는 로컬 변수라는 경고문도 띄우지 않습니다. 균일 초기화 구문을 적용하면 선언한 적 없는 m이란 식별자에 대한 컴파일 에러는 발생 시킬 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;unique_lock&amp;lt;mutex&amp;gt; (m);&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;RAII 패턴을 이용한 크리티컬 섹션 관리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAII 패턴을 사용하지 않을 때, 개발자는 직접 크리티컬 섹션의 진입과 탈출을 관리해야 합니다. 이는 코드의 복잡성을 증가시키고, 오류 발생의 가능성을 높일 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;#include &amp;lt;windows.h&amp;gt;

CRITICAL_SECTION cs;

void someFunction() {
    EnterCriticalSection(&amp;amp;cs);  // 크리티컬 섹션 진입
    // 공유 자원을 사용하는 코드
    LeaveCriticalSection(&amp;amp;cs);  // 크리티컬 섹션 탈출
}

int main() {
    InitializeCriticalSection(&amp;amp;cs);
    someFunction();
    DeleteCriticalSection(&amp;amp;cs);
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 &lt;code&gt;someFunction&lt;/code&gt; 함수 내에서 크리티컬 섹션을 직접 관리하고 있습니다. 만약 &lt;code&gt;someFunction&lt;/code&gt; 함수 내의 코드에서 예외가 발생하면 &lt;code&gt;LeaveCriticalSection&lt;/code&gt;가 호출되지 않을 수도 있습니다. 이는 크리티컬 섹션의 락이 해제되지 않아 다른 스레드가 영구적으로 차단될 수 있는 상황을 초래할 수 있습니다. 또한 크리티컬 섹션을 사용할 경우, 각 함수마다 진입과 탈출 코드를 반복적으로 작성해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAII 패턴을 적용하면, 크리티컬 섹션의 lock(EnterCriticalSection)과 unlock(LeaveCriticalSection)을 자동화와 해주고 중복 코드도 방지할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;mutex&amp;gt;

class CriticalSection {
public:
    CriticalSection() {
        InitializeCriticalSection(&amp;amp;m_cs);  // CRITICAL_SECTION 초기화
    }

    ~CriticalSection() {
        DeleteCriticalSection(&amp;amp;m_cs);      // 자원 해제
    }

    void lock() {
        EnterCriticalSection(&amp;amp;m_cs);       // 임계 영역 진입
    }

    void unlock() {
        LeaveCriticalSection(&amp;amp;m_cs);       // 임계 영역 해제
    }

    // 복사 생성자와 할당 연산자 삭제하여 복사 방지
    CriticalSection(const CriticalSection&amp;amp;) = delete;
    CriticalSection&amp;amp; operator=(const CriticalSection&amp;amp;) = delete;

private:
    CRITICAL_SECTION m_cs;                // 내부 CRITICAL_SECTION 객체
};


CriticalSection critical_section;
void someFunction() {
    std::lock_guard&amp;lt;CriticalSection&amp;gt; lock(critical_section); // RAII 기반 자동 잠금
    // 여기에 임계 영역 코드 작성
} // lock 객체의 스코프가 끝나면 자동으로 CriticalSection의 unlock 메소드 실행

int main() {
    someFunction(); // 함수 호출
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드에서 &lt;code&gt;CriticalSection&lt;/code&gt; 클래스는 크리티컬 섹션의 생성, 파괴 및 잠금 관리를 자동으로 처리합니다. &lt;code&gt;std::lock_guard&lt;/code&gt;를 사용함으로써 함수가 종료되거나 예외가 발생할 때 자동으로 크리티컬 섹션을 해제시켜 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;std::lock_guard란?&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;template &amp;lt;class _Mutex&amp;gt;
class _NODISCARD lock_guard { // class with destructor that unlocks a mutex
public:
    using mutex_type = _Mutex;

    explicit lock_guard(_Mutex&amp;amp; _Mtx) : _MyMutex(_Mtx) { // construct and lock
        _MyMutex.lock();
    }

    lock_guard(_Mutex&amp;amp; _Mtx, adopt_lock_t) : _MyMutex(_Mtx) {} // construct but don't lock

    ~lock_guard() noexcept {
        _MyMutex.unlock();
    }

    lock_guard(const lock_guard&amp;amp;) = delete;
    lock_guard&amp;amp; operator=(const lock_guard&amp;amp;) = delete;

private:
    _Mutex&amp;amp; _MyMutex;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;std::lock_guard는 에 정의된 간단한 락으로서, 다음 두 가지 생성자를 제공합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;exlicit lock_guard(mutex_type&amp;amp; m);
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;뮤텍스에 대한 래퍼런스를 인수로 받는 생성자&lt;/li&gt;
&lt;li&gt;이 생성자는 전달된 뮤텍스에 락을 걸기 위해 시도하고, 완전히 락이 걸릴 때까지 블록&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;lock_guard(mutex_type&amp;amp; m, adopt_lock_t);
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;뮤텍스에 대한 래퍼런스와 std::adopt_lock_t의 인스턴스를 인수로 받는 생성자&lt;/li&gt;
&lt;li&gt;std::adopt_lock이라는 이름으로 미리 정의된 adopt_lock_t 인스턴스가 제공됩니다. 이때 호출하는 측의 스레드는 인수로 지정한 뮤텍스에 대한 락을 이미 건 상태에서 추가로 락을 겁니다. 락이 제거되면 뮤텍스도 자동으로 해제됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OpenAI의 ChatGPT :&lt;a href=&quot;https://chatgpt.com/&quot;&gt;https://chatgpt.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;RAII 패턴
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/ko-kr/cpp/cpp/object-lifetime-and-resource-management-modern-cpp?view=msvc-170&quot;&gt;https://learn.microsoft.com/ko-kr/cpp/cpp/object-lifetime-and-resource-management-modern-cpp?view=msvc-170&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://modoocode.com/category/C++&quot;&gt;https://modoocode.com/category/C++&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://codereview.stackexchange.com/questions/182702/two-layer-raii-to-support-a-critical-section&quot;&gt;https://codereview.stackexchange.com/questions/182702/two-layer-raii-to-support-a-critical-section&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.yes24.com/Product/Goods/117986059&quot;&gt;https://www.yes24.com/Product/Goods/117986059&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>[C++]</category>
      <category>c++</category>
      <category>Critical Section</category>
      <category>크리티컬 섹션</category>
      <author>handr95</author>
      <guid isPermaLink="true">https://handr95.tistory.com/43</guid>
      <comments>https://handr95.tistory.com/43#entry43comment</comments>
      <pubDate>Sat, 11 May 2024 23:44:49 +0900</pubDate>
    </item>
    <item>
      <title>[VS] Visual Studio 2022 Jetbrains Mono  폰트 적용하기</title>
      <link>https://handr95.tistory.com/42</link>
      <description>&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;사이트에 접속하여 글꼴을 다운 받는다.&lt;/b&gt; &lt;a href=&quot;https://www.jetbrains.com/ko-kr/lp/mono/&quot;&gt;(https://www.jetbrains.com/ko-kr/lp/mono/)&lt;/a&gt;&lt;b&gt;&lt;/b&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;636&quot; data-origin-height=&quot;389&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYkmit/btsGKcRMu5X/41KWdObQz7ct7fkL8b2Ex0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYkmit/btsGKcRMu5X/41KWdObQz7ct7fkL8b2Ex0/img.png&quot; data-alt=&quot;[글꼴 다운로드]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYkmit/btsGKcRMu5X/41KWdObQz7ct7fkL8b2Ex0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYkmit%2FbtsGKcRMu5X%2F41KWdObQz7ct7fkL8b2Ex0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;636&quot; height=&quot;389&quot; data-origin-width=&quot;636&quot; data-origin-height=&quot;389&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[글꼴 다운로드]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다운 받은 파일의 압축을 풀고 ttf 폴더로 이동한다.&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (1).png&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;362&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TtQqk/btsGJmtNIB4/FgPu62mnwwOqb0HMRx5WBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TtQqk/btsGJmtNIB4/FgPu62mnwwOqb0HMRx5WBk/img.png&quot; data-alt=&quot;폴더 내 파일 전체 선택 후 [오른쪽 마우스 클릭] - [설치]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TtQqk/btsGJmtNIB4/FgPu62mnwwOqb0HMRx5WBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTtQqk%2FbtsGJmtNIB4%2FFgPu62mnwwOqb0HMRx5WBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;626&quot; height=&quot;362&quot; data-filename=&quot;Untitled (1).png&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;362&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;폴더 내 파일 전체 선택 후 [오른쪽 마우스 클릭] - [설치]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. &lt;b&gt;Visual Studio 로 돌아와서 [도구] - [옵션 클릭]&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (2).png&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;474&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9zIQB/btsGJODBygL/XzTdkwjM1wJ6TMoMuW5TS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9zIQB/btsGJODBygL/XzTdkwjM1wJ6TMoMuW5TS1/img.png&quot; data-alt=&quot;[도구] - [옵션 클릭]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9zIQB/btsGJODBygL/XzTdkwjM1wJ6TMoMuW5TS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9zIQB%2FbtsGJODBygL%2FXzTdkwjM1wJ6TMoMuW5TS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;819&quot; height=&quot;474&quot; data-filename=&quot;Untitled (2).png&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;474&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[도구] - [옵션 클릭]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. &lt;b&gt;검색창에 &amp;lsquo;글꼴&amp;rsquo; 검색&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (3).png&quot; data-origin-width=&quot;744&quot; data-origin-height=&quot;437&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TZQV1/btsGHEPG60w/fqM7Cd06nuT6ucjOA9JE40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TZQV1/btsGHEPG60w/fqM7Cd06nuT6ucjOA9JE40/img.png&quot; data-alt=&quot;'글꼴' 셀렉트 박스에서 JetBrains Mono 선택 - [확인] 클릭&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TZQV1/btsGHEPG60w/fqM7Cd06nuT6ucjOA9JE40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTZQV1%2FbtsGHEPG60w%2FfqM7Cd06nuT6ucjOA9JE40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;744&quot; height=&quot;437&quot; data-filename=&quot;Untitled (3).png&quot; data-origin-width=&quot;744&quot; data-origin-height=&quot;437&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;'글꼴' 셀렉트 박스에서 JetBrains Mono 선택 - [확인] 클릭&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. &lt;b&gt;폰트 적용 완료 &lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (4).png&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;527&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzQDrw/btsGHkjDgEy/4YPWQvKv6V8tEq0V5zspc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzQDrw/btsGHkjDgEy/4YPWQvKv6V8tEq0V5zspc0/img.png&quot; data-alt=&quot;이음자가 적용되어있다면 적용 성공&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzQDrw/btsGHkjDgEy/4YPWQvKv6V8tEq0V5zspc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzQDrw%2FbtsGHkjDgEy%2F4YPWQvKv6V8tEq0V5zspc0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;819&quot; height=&quot;527&quot; data-filename=&quot;Untitled (4).png&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;527&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이음자가 적용되어있다면 적용 성공&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;※ 폰트 적용이 안되었다면 Visual Studio 재실행 후 다시 확인&lt;/i&gt;&lt;/p&gt;</description>
      <category>[기타]</category>
      <category>IntelliJ</category>
      <category>JetBrains Mono</category>
      <category>Visual Studio 2022</category>
      <author>handr95</author>
      <guid isPermaLink="true">https://handr95.tistory.com/42</guid>
      <comments>https://handr95.tistory.com/42#entry42comment</comments>
      <pubDate>Wed, 17 Apr 2024 23:01:17 +0900</pubDate>
    </item>
    <item>
      <title>[Java] switch 표현식</title>
      <link>https://handr95.tistory.com/41</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;전형적인 switch 표현식&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1679391056306&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@NoArgsConstructor
@AllArgsConstructor
public class Player {
    private int id;
    private PlayerTypes type;
    
    public static Player createPlayer(PlayerTypes type){
        switch (type) {
            case TENNIS : 
                return new TennisPlayer();
            case FOOTBALL :
                return new FootballPlayer();
            case UNKNOWN :
                return new UnknownPlayerException(&quot;Player type is unknown&quot;);
            // default문을 빠뜨리면 컴파일 에러가 발생됨
            default:
                throw new IllegalArgumentException(&quot;invalid type : &quot; + type);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;default가 빠진 문제 있는 표현식&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1679391180010&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@NoArgsConstructor
@AllArgsConstructor
public class Player {
    private int id;
    private PlayerTypes type;
    
    public static Player createPlayer(PlayerTypes type){
    	Player player = null;
        switch (type) {
            case TENNIS : 
                Player player = new TennisPlayer();
            case FOOTBALL :
                Player player = new FootballPlayer();
            case UNKNOWN :
                return new UnknownPlayerException(&quot;Player type is unknown&quot;);
            // default가 빠져있더라도 컴파일 오류를 내지 않음
        }
        return player;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때는 defualt를 빠뜨려도 컴파일 에러가 발생되지 않는다. 누락된 default문으로 player에 null이 그대로 반환 될수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;개선된 switch 표현식 - JDK 12&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1679391436818&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static Player createPlayer(PlayerTypes type){
    return switch (type) {
        case TENNIS -&amp;gt; new TennisPlayer();
        case FOOTBALL -&amp;gt; new FootballPlayer();
        case UNKNOWN -&amp;gt; throw new UnknownPlayerException(&quot;invalid type : &quot; + type);
        // default는 생략 가능
        default -&amp;gt; throw new IllegalArgumentException(&quot;invalid type : &quot; + type);
    };
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JDK12 switch는 enum 값을 전부 다뤘는지 감지할 수 있고 defulat가 불필요하면 강요하지 않는다. 가령 default를 제거하고 PlayerTypes enum에 새 항목을 추가하면 컴파일러는 에러 메시지를 출력한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1005&quot; data-origin-height=&quot;478&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brOI7E/btr45mFm1te/RUtBPUe7ChxHEIrmdlIgl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brOI7E/btr45mFm1te/RUtBPUe7ChxHEIrmdlIgl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brOI7E/btr45mFm1te/RUtBPUe7ChxHEIrmdlIgl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrOI7E%2Fbtr45mFm1te%2FRUtBPUe7ChxHEIrmdlIgl0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1005&quot; height=&quot;478&quot; data-origin-width=&quot;1005&quot; data-origin-height=&quot;478&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 람다 스타일의 문법이 아닌 콜론(:)과 break문도 사용 가능 하다&lt;/p&gt;
&lt;pre id=&quot;code_1679391747988&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static Player createPlayer(PlayerTypes type){
    return switch (type) {
        case TENNIS :
            break new TennisPlayer();
        case FOOTBALL :
            break new FootballPlayer();
        case UNKNOWN :
            throw new UnknownPlayerException(&quot;Player type is unknown&quot;);
    };
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jdk13부터는 break문이 yield로 대체되어 아래와 같이 사용해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1679391789092&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static Player createPlayer(PlayerTypes type){
    return switch (type) {
        case TENNIS :
            yield new TennisPlayer();
        case FOOTBALL :
            yield new FootballPlayer();
        case UNKNOWN :
            throw new UnknownPlayerException(&quot;Player type is unknown&quot;);
    };
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;다수의 case 레이블 - JDK 12&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jdk 12 이전의 switch 문은 case당 하나의 레이블을 허용했지만, jdk 12부터는 case에 콤마로 구분된 다수의 레이블을 둘 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1679391908136&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static Player createPlayer(PlayerTypes type){
    return switch (type) {
        case TENNIS, FOOTBALL, SNOOKER
            -&amp;gt; new Team();
        case UNKNOWN 
            -&amp;gt; new Unknown();
    };
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;명령문 블록 - JDK 12&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1679392065158&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static Player createPlayer(PlayerTypes type){
    return switch (type) {
    	// 명령문을 중괄호로 묶어 처리
        case TENNIS, FOOTBALL, SNOOKER -&amp;gt; {
            System.out.println(&quot;create new team&quot;);
            break new Team();  
        } 
        case UNKNOWN -&amp;gt; {
            System.out.println(&quot;create new unknown&quot;);
            break new Unknown();
        }
    };
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jdk 13이후 버전은 아래처럼 사용해줘야합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1679392127982&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static Player createPlayer(PlayerTypes type){
    return switch (type) {
    	// 명령문을 중괄호로 묶어 처리
        case TENNIS, FOOTBALL, SNOOKER -&amp;gt; {
            System.out.println(&quot;create new team&quot;);
            yield new Team();  
        } 
        case UNKNOWN -&amp;gt; {
            System.out.println(&quot;create new unknown&quot;);
            yield new Unknown();
        }
    };
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 return이 아닌 break/yield로 중괄호 블록을 빠져나왔다. switch 문에서는 return문을 사용할수는 있지만 중괄호 구문에서는 return을 사용하지 못한다. 이를 주의하여 사용하자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;http://www.yes24.com/Product/Goods/113416654&quot;&gt;http://www.yes24.com/Product/Goods/113416654&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mkyong.com/java/java-12-switch-expressions&quot;&gt;https://mkyong.com/java/java-12-switch-expressions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mkyong.com/java/java-13-switch-expressions&quot;&gt;https://mkyong.com/java/java-13-switch-expressions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>[JAVA]</category>
      <category>java</category>
      <category>jdk12</category>
      <category>jdk13</category>
      <category>Switch</category>
      <author>handr95</author>
      <guid isPermaLink="true">https://handr95.tistory.com/41</guid>
      <comments>https://handr95.tistory.com/41#entry41comment</comments>
      <pubDate>Tue, 21 Mar 2023 21:51:02 +0900</pubDate>
    </item>
    <item>
      <title>[Java] 자바 스레드와 동시성(5) - Thread Pool (스레드풀), Executor</title>
      <link>https://handr95.tistory.com/39</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Thread 병렬 작업 시 문제점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 몇 개의 과정에서 Thread를 생성하고 실행하는 방법에 대해 알아보았습니다. 이런 식으로 Thread를 실행했을 때 생기는 중요한 결핍 중 하나는 Thread 실행에 대해서 그 어떤 통제도 불가능하다는 점이 있습니다. 어느 특정 시점에서 얼마나 많은 Thread가 실행되고 있는지 알 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 세 개의 Thread만 어떤 시점에서 실행하고 있게 만들고 싶다고 해보겠습니다. 이러한 조작은 Thread의 실행을 start 메소드를 이용하여 확립할 경우에는 매우 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 문제도 있습니다. 예를 들어 Task1과 Task2 중 하나가 완료될 때까지 기다리고 싶다고 해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thread를 이용한 기본적인 것으로는 이 상황에 맞는 코드를 작성하는 것이 불가능합니다. 그러므로 Task1 또는 Task2가 실행이 완료될 때까지 기다리는 작업을 하고 싶다고 해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;task1.join();
task2Thread.join();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 코드를 작성하면, Task1과 Task2가 모두 완료될 때까지 기다렸다가 다음 코드가 실행되는 것을 확인 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 만약 Task1 혹은 Task2 중 하나가 완료된 후에 실행할 수 있도록 하는 코드를 작성하고 싶다면, join() 메소드 만으로는 구현이 불가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 Thread를 실행하는 과정은 매우 지루한 과정입니다. 만약에 100개의 task를 실행하고 싶다면, 이와 같은 코드(&lt;span style=&quot;background-color: #dddddd;&quot;&gt;Task1 task1 = new Task1(); task1.setPriority(1); task1.start();&lt;/span&gt;)를 모두 작성한 후에 시작을 실행해 주시면 됩니다. 다시, 이 모든 코드를 작성하고, 실행을 눌러주시면 됩니다. 이 코드의 묶음의 형식으로 처리하는 방법은 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로, 가끔 Thread가 실행한 task의 결과를 반환하고 싶은 경우가 생길 수 있습니다. 우리가 지금까지 배운 이야기한 내용으로는 불가능한 내용입니다. 여기서 Executor Service, 즉 실행기를 사용할 수 있습니다. Executor Service는 지금까지 이야기 나눈 이 모든 기능을 아우를 수 있는 기능들을 제공합니다. 다수의 Thread를 한 번에 실행할 수 있고, Thread의 상태를 알 수 있도록 도와주며, 다수의 Thread를 한 번에 실행할 수 있고, Thread의 상태를 알 수 있도록 도와주며, Thread와 사용자가 다음과 같은 논리를 실행할 수 있도록 도와줍니다. Thread1 혹은 Thread2 혹은 Thread3가 완료되면 알려주도록 할 수 있습니다. 이들 중 어떤 Thread가 먼저 실행이 완료되었는지 알 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 모든 Thread가 실행이 완료될 때까지 기다리도록 단 한 문장으로 코드를 작성할 수 있습니다. 이 모든 것들이 &lt;b&gt;Executor Service&lt;/b&gt;로 구현 가능한 것들입니다. 다음 단계에서는 Executor Service에 대한 내용을 시작해 보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Executor Service에 대해 설명하기 전에 스레드풀(ThreadPool)에 대해 알아야하므로, &lt;b&gt;ThreadPool&lt;/b&gt;에 대해 먼저 알아보도록하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Thread Pool 이 나온 배경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 및 웹 서버와 같은 서버 프로그램은 여러 클라이언트의 요청을 반복적으로 실행하며 많은 수의 작은 작업들을 처리하는 데 중점을 둡니다. 서버 응용 프로그램을 이러한 요청을 처리하는 방식은 요청이 도착할 때마다 새 스레드를 만들고 새로 만든 스레드에서 받은 요청을 처리하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 방식은 구현이 간단해 보이지만 큰 단점이 있습니다. 모든 요청에 대해 새 스레드를 생성하는 서버는 실제 요청을 처리하는 것보다 스레드 생성 및 소멸에 더 많은 시간을 소비하고 더 많은 시스템 리소스를 소비합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;활성(active) 스레드는 시스템 리소스를 소비하기 때문에 동시에 너무 많은 스레드를 생성하는 JVM은 시스템의 메모리 부족(out of memory)을 유발할 수 있습니다. 이것은 생성되는 쓰레드의 수를 제한할 필요성을 느끼게 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Thread Pool 이란&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드 풀은 이전에 생성된 스레드를 재사용하여 현재 작업을 실행하고 스레드 사이클 오버 헤드(thread cycle overhead) 및 리소스 스래싱(resource thrashing) 문제에 대한 해결책을 제공합니다. 요청이 도착하면 스레드가 이미 존재하기 때문에 스레드 생성으로 인한 지연이 제거되어 애플리케이션이 보다 신속하게 응답 할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java는 Executor 인터페이스(하위 인터페이스인 ExecutorService나, ThreadPoolExecutor 및 이 두 인터페이스를 모두 구현하는 클래스들)를 중심으로 하는 Executor 프레임워크를 제공합니다. Executor를 사용하면 Runnable 객체를 구현하고 Executor로 보내 실행하기만 하면 됩니다.&lt;/li&gt;
&lt;li&gt;이를 통해 스레딩을 활용할 수 있게 해줍니다. Executor에서 스레드를 관리해주기 때문에 스레드의 내부 동작을 보다는 수행하려는 작업에 집중할 수 있습니다.&lt;/li&gt;
&lt;li&gt;스레드 풀을 사용하려면 먼저 ExecutorService의 객체를 만들고 작업셋을 전달합니다. ThreadPoolExecutor 클래스를 사용하면 코어 및 최대 풀 크기를 설정할 수 있습니다. 특정 스레드에서 실행되는 실행 가능한 항목은 순차적으로 실행됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYCH7c/btrN3ewMJYq/JRaQAITRgfa5U4NFi0CDr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYCH7c/btrN3ewMJYq/JRaQAITRgfa5U4NFi0CDr1/img.png&quot; data-alt=&quot;Thread Pool Initialization with size = 3 threads. Task Queue = 5 Runnable Objects&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYCH7c/btrN3ewMJYq/JRaQAITRgfa5U4NFi0CDr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYCH7c%2FbtrN3ewMJYq%2FJRaQAITRgfa5U4NFi0CDr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;769&quot; height=&quot;436&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;436&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Thread Pool Initialization with size = 3 threads. Task Queue = 5 Runnable Objects&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. &lt;b&gt;Executor Thread Pool Methods&lt;/b&gt;&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.2403%;&quot;&gt;메소드&lt;/td&gt;
&lt;td style=&quot;width: 50.4263%;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.2403%;&quot;&gt;newFixedThreadPool(int)&lt;/td&gt;
&lt;td style=&quot;width: 50.4263%;&quot;&gt;고정된&amp;nbsp;크기의&amp;nbsp;스레드&amp;nbsp;풀을&amp;nbsp;생성합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.2403%;&quot;&gt;newCachedThreadPool()&lt;/td&gt;
&lt;td style=&quot;width: 50.4263%;&quot;&gt;필요에&amp;nbsp;따라&amp;nbsp;새&amp;nbsp;스레드를&amp;nbsp;생성하는&amp;nbsp;스레드&amp;nbsp;풀을&amp;nbsp;생성하지만&amp;nbsp;사용&amp;nbsp;가능한&amp;nbsp;경우&amp;nbsp;이전에&amp;nbsp;생성된&amp;nbsp;스레드를&amp;nbsp;재사용합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.2403%;&quot;&gt;newSingleThreadExecutor()&lt;/td&gt;
&lt;td style=&quot;width: 50.4263%;&quot;&gt;단일&amp;nbsp;스레드를&amp;nbsp;생성합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고정 스레드 풀의 경우, 현재 모든 스레드가 executor에 의해 실행(스레드 풀에 있는모든 스레드가 active 상태일 경우)되고 있으면 추가로 들어온 작업들은 대기열에 배치되고 스레드가 유휴 상태가 되면 실행됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. &lt;b&gt;Executor Thread Pool Methods&lt;/b&gt; 예제&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.1 newSimgleThreadExecutor()&lt;/h3&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorServiceMain {
    public static void main(String[] args) {
        // 1. ExecutorService 생성. newSingleThreadExecutor : 한번에 하나의 Thread를 실행하도록 하는 기능을 제공
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        // 2. ExecutorService에 실행할 task를 넘겨줌
        executorService.execute(new Task1());
        executorService.execute(new Thread(new Task2()));

        // 3. ExecutorService 종료. 해당 처리를 해주지 않으면 프로그램이 종료되지 않음
        executorService.shutdown();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Task1 Start
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 
Task1 Finish

Task2 Start
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 
Task2 Finish
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 번에 한 개의 스레드만 실행 할수 있기 때문에 Task1이 종료 된 후에 Task2가 실행되는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorServiceMain {
    public static void main(String[] args) {
        // 1. ExecutorService 생성. newSingleThreadExecutor : 한번에 하나의 Thread를 실행하도록 하는 기능을 제공
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        // 2. ExecutorService에 실행할 task를 넘겨줌
        executorService.execute(new Task1());
        executorService.execute(new Thread(new Task2()));

        //Task3
        System.out.println(&quot;Task3 Start&quot;);
        for(int i = 301; i &amp;lt;= 399; i++) {
            System.out.print(i + &quot; &quot;);
        }
        System.out.println(&quot;\\nTask3 Finish&quot;);

        // 3. ExecutorService 종료. 해당 처리를 해주지 않으면 프로그램이 종료되지 않음
        executorService.shutdown();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Task1 Start
Task3 Start
101 102 103 104 105 106 301 107 302 108 303 109 304 110 305 306 307 111 308 112 309 113 310 114 311 115 312 116 313 117 314 315 118 316 317 318 319 119 320 120 321 121 322 122 323 123 324 124 325 125 326 126 327 328 127 329 128 330 129 331 130 332 131 333 132 334 133 335 134 336 135 337 136 338 339 137 340 341 138 342 343 344 345 346 347 348 349 350 351 352 139 140 141 353 142 354 143 355 144 356 145 357 146 358 147 359 148 360 149 361 150 362 363 364 365 366 367 151 368 369 370 371 372 373 152 374 153 375 154 376 155 377 156 157 158 159 160 161 378 162 379 163 380 164 165 166 167 381 168 382 169 383 170 384 171 172 385 173 386 174 387 175 176 177 178 179 388 180 389 181 390 182 391 183 184 185 186 187 188 189 190 191 192 193 194 392 195 393 196 197 198 394 395 396 397 199 398 
Task1 Finish
399 
Task2 Start

Task3 Finish
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 
Task2 Finish
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Task1 시작되고 Task3가 시작된 것을 확인 할 수 있고. 위 예제와 동일하게 Task1의 실행이 종료된 후에 Task2가 실행된 것을 알 수 있습니다. Task1과 Task2는 executorService를 통해 실행되었고, Task3는 main 메소드에서 실행되었습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.2 newFixedThreadPool(int)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실행 플로우&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1. 실행할 태스크(Runnable Object) 생성
2. Executor를 사용하여 Executor Pool 생성
3. Executor Pool에 작업 전달
4. 실행자 풀 종료
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;코드&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

// 1. 실행할 태스크(Runnable Object) 생성
class Task implements Runnable {
    private String name;
    public Task(String s) {
        name = s;
    }

    public void run() {
        try {

            // name 필드값을 출력하고 1초 동안 sleep() 5회 반복
            for (int i = 0; i &amp;lt;= 5; i++) {
                if (i == 0) {
                    Date d = new Date();
                    SimpleDateFormat ft = new SimpleDateFormat(&quot;hh:mm:ss&quot;);
                    System.out.println(&quot;Initialization Time for task name - &quot; + name + &quot; = &quot; + ft.format(d));
                } else {
                    Date d = new Date();
                    SimpleDateFormat ft = new SimpleDateFormat(&quot;hh:mm:ss&quot;);
                    System.out.println(&quot;Executing Time for task name - &quot; + name + &quot; = &quot; + ft.format(d));
                }
                Thread.sleep(1000);
            }
            System.out.println(name + &quot; complete&quot;);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class FixedThreadMain {
    static final int MAX_T = 3; // 스레드 풀의 최대 스레드 수

    public static void main(String[] args) {
        // Task 5개 생성
        Runnable r1 = new Task(&quot;task 1&quot;);
        Runnable r2 = new Task(&quot;task 2&quot;);
        Runnable r3 = new Task(&quot;task 3&quot;);
        Runnable r4 = new Task(&quot;task 4&quot;);
        Runnable r5 = new Task(&quot;task 5&quot;);

        // 2. MAX_T값 만큼 스레드 풀을 생성합니다. 이 때 스레드 풀내 스레드 개수는 고정되어있음
        ExecutorService pool = Executors.newFixedThreadPool(MAX_T);

        // 3. Task 개체를 풀에 전달하여 실행할 수 있습니다.
        pool.execute(r1);
        pool.execute(r2);
        pool.execute(r3);
        pool.execute(r4);
        pool.execute(r5);

        // 4. pool shutdown
        pool.shutdown();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Initialization Time for task name - task 3 = 05:02:17
Initialization Time for task name - task 1 = 05:02:17
Initialization Time for task name - task 2 = 05:02:17
Executing Time for task name - task 3 = 05:02:18
Executing Time for task name - task 2 = 05:02:18
Executing Time for task name - task 1 = 05:02:18
Executing Time for task name - task 1 = 05:02:19
Executing Time for task name - task 2 = 05:02:19
Executing Time for task name - task 3 = 05:02:19
Executing Time for task name - task 3 = 05:02:20
Executing Time for task name - task 1 = 05:02:20
Executing Time for task name - task 2 = 05:02:20
Executing Time for task name - task 1 = 05:02:21
Executing Time for task name - task 2 = 05:02:21
Executing Time for task name - task 3 = 05:02:21
Executing Time for task name - task 2 = 05:02:22
Executing Time for task name - task 1 = 05:02:22
Executing Time for task name - task 3 = 05:02:22
task 3 complete
task 2 complete
task 1 complete
Initialization Time for task name - task 5 = 05:02:23
Initialization Time for task name - task 4 = 05:02:23
Executing Time for task name - task 4 = 05:02:24
Executing Time for task name - task 5 = 05:02:24
Executing Time for task name - task 4 = 05:02:25
Executing Time for task name - task 5 = 05:02:25
Executing Time for task name - task 5 = 05:02:26
Executing Time for task name - task 4 = 05:02:26
Executing Time for task name - task 5 = 05:02:27
Executing Time for task name - task 4 = 05:02:27
Executing Time for task name - task 5 = 05:02:28
Executing Time for task name - task 4 = 05:02:28
task 4 complete
task 5 complete
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램 실행에서 알 수 있듯이 Task4와 Task5는 스레드 풀 내의 스레드가 유휴 상태가 될 때만 실행됩니다. 그때까지 추가 작업은 대기열에 배치됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;571&quot; data-origin-height=&quot;371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjqAtv/btrN4OcicK4/OLmvAkMOVr2epdrihoE21k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjqAtv/btrN4OcicK4/OLmvAkMOVr2epdrihoE21k/img.png&quot; data-alt=&quot;Thread Pool executing first three tasks&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjqAtv/btrN4OcicK4/OLmvAkMOVr2epdrihoE21k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjqAtv%2FbtrN4OcicK4%2FOLmvAkMOVr2epdrihoE21k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;571&quot; height=&quot;371&quot; data-origin-width=&quot;571&quot; data-origin-height=&quot;371&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Thread Pool executing first three tasks&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;571&quot; data-origin-height=&quot;371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/enutZc/btrN3VcbTGJ/aGLldYHHSZFhN0AhAk0RcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/enutZc/btrN3VcbTGJ/aGLldYHHSZFhN0AhAk0RcK/img.png&quot; data-alt=&quot;Thread Pool executing task 4 and 5&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/enutZc/btrN3VcbTGJ/aGLldYHHSZFhN0AhAk0RcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FenutZc%2FbtrN3VcbTGJ%2FaGLldYHHSZFhN0AhAk0RcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;571&quot; height=&quot;371&quot; data-origin-width=&quot;571&quot; data-origin-height=&quot;371&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Thread Pool executing task 4 and 5&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  ThreadPool을 사용할 때의 주요 이점 중 하나는 한 번에 100개의 요청을 처리하고 싶지만 JVM 과부하를 줄이기 위해 동일한 요청에 대해 100개의 스레드를 생성하고 싶지 않을 때입니다. 이 방식을 사용하여 10개의 스레드로 구성된 ThreadPool을 만들고 이 ThreadPool에 100개의 요청을 제출할 수 있습니다. ThreadPool은 한 번에 10개의 요청을 처리하기 위해 최대 10개의 스레드를 생성합니다. 단일 스레드의 프로세스 완료 후, ThreadPool은 내부적으로 이 스레드에 11번째 요청을 할당합니다. 나머지 모든 요청에 대해 계속 동일한 작업을 수행합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. Thread Pool 문제점&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;교착 상태(Deadlock)&lt;/b&gt;: 모든 멀티 스레드 프로그램에서 교착 상태가 발생할 수 있지만 스레드 풀내의 스레드에서 교착 상태 발생 시 또 다른 교착 상태를 유발합니다. 이 경우 실행 중인 모든 실행 스레드가 실행을 위해 스레드를 사용할 수 없어 대기열에서 대기 중인 블락된 스레드의 결과를 계속 대기 하고 있는 문제가 발생합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스레드 누수(Thread Leakage)&lt;/b&gt; : Thread Leakage는 스레드가 풀에서 제거되어 작업을 실행하지만 반환되지 않은 경우 발생합니다. 예를 들어 스레드가 예외를 throw하고 풀 클래스가 이 예외를 catch하지 않으면 스레드는 종료되어 스레드 풀의 크기가 1 감소합니다. 이 작업이 여러 번 반복되면 결국 풀이 비어 있게 되고 다른 요청을 실행하는 데 사용할 수 있는 스레드가 없어지게 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;리소스 스레싱(Resource Thrashing)&lt;/b&gt;: 스레드 풀 크기가 매우 크면 스레드 간 컨텍스트 전환에 시간이 낭비됩니다. 최적의 수보다 많은 스레드가 있으면 설명된 대로 자원 낭비로 이어지는 기아 문제(starvation problem)가 발생할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. Thread Pool 사용 시 주의 사항&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 작업의 결과를 동시에 기다리는 작업을 대기열에 넣지 마십시오. 이로 인해 위에서 설명한 교착 상태가 발생할 수 있습니다.&lt;/li&gt;
&lt;li&gt;수명이 긴 작업에 스레드를 사용한다면 주의 깊게 살펴봐야합니다. 이로 인해 스레드가 영원히 대기하고 결국 리소스 누수가 발생할 수 있습니다.&lt;/li&gt;
&lt;li&gt;스레드 풀은 마지막에 명시적으로 종료되어야 합니다. 이 작업을 하지 않으면 프로그램이 계속 실행되고 끝나지 않습니다. 풀에서 shutdown()을 호출하여 Excutor를 종료합니다. 종료 후 이 Excutor에 다른 작업을 보내려고 하면 RejectedExecutionException이 발생합니다.&lt;/li&gt;
&lt;li&gt;스레드 풀을 효과적으로 조정하려면 TasK를 이해해야 합니다. 동일한 스레드 풀을 사용하는 Task가 서로 매우 대조적이라면 작업을 적절하게 조정하기 위해 Task 유형에 따라 다른 스레드 풀을 사용하는 것이 좋습니다.&lt;/li&gt;
&lt;li&gt;JVM에서 실행할 수 있는 최대 스레드 수를 제한하여 JVM의 메모리 부족 가능성을 줄이는 것이 좋습니다.&lt;/li&gt;
&lt;li&gt;처리를 위해 새 스레드를 생성하는 반복문을 구현해야 하는 경우 ThreadPool이 최대 제한에 도달한 후 새 스레드를 생성하지 않기 때문에 ThreadPool을 사용하면 더 빠르게 처리하는 데 도움이 됩니다.&lt;/li&gt;
&lt;li&gt;스레드 처리가 완료된 후 ThreadPool은 동일한 스레드를 사용하여 다른 프로세스를 수행할 수 있습니다. (따라서 다른 스레드를 생성하는 시간과 리소스를 절약할 수 있습니다.)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. Thread Pool 튜닝&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드 풀의 최적 크기는 사용 가능한 프로세서의 수와 작업의 특성에 따라 다릅니다. 계산(computation) 유형 프로세스만 있는 대기열에 대한 N개의 프로세서(CPU) 시스템에서 N 또는 N+1로 최대 스레드 풀 사이즈를 설정하면 최대 효율성을 달성할 수 있습니다. 그러나 작업은 I/O를 기다릴 수 있으며 이러한 경우 비율을 고려합니다. 따라서 아래와 같이 아래의 계산식에 따라 적정 스레드 수를 맞춰주는 것이 좋습니다&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적정 스레드 수 = 사용 가능한 CPU 코어 수 * (1 + 요청에 대한 대기 시간/서비스 시간)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드 풀은 서버 응용 프로그램을 구성하는 데 유용한 도구입니다. 개념적으로는 매우 간단하지만 교착 상태, 리소스 스래싱과 같이 구현 및 사용할 때 주의해야 할 몇 가지 문제가 있습니다. Executor Service를 사용하면 구현이 더 쉬워집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/deadlock-in-java-multithreading/&quot;&gt;https://www.geeksforgeeks.org/thread-pools-java/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.baeldung.com/thread-pool-java-and-guava&quot;&gt;https://www.baeldung.com/thread-pool-java-and-guava&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>[JAVA]</category>
      <category>Executor</category>
      <category>java</category>
      <category>Thread</category>
      <category>ThreadPool</category>
      <author>handr95</author>
      <guid isPermaLink="true">https://handr95.tistory.com/39</guid>
      <comments>https://handr95.tistory.com/39#entry39comment</comments>
      <pubDate>Mon, 10 Oct 2022 22:51:10 +0900</pubDate>
    </item>
    <item>
      <title>[Java] 자바 스레드와 동시성(4) - join(), sleep(), yield()</title>
      <link>https://handr95.tistory.com/38</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Thread.join()&lt;/h2&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;class Task1 extends Thread {
    @Override
    public void run() {
        System.out.println(&quot;\\nTask1 Start&quot;);
        for(int i = 101; i &amp;lt;= 199; i++) {
            System.out.print(i + &quot; &quot;);
        }
        System.out.println(&quot;\\nTask1 Finish&quot;);
    }
}

class Task2 implements Runnable {
    @Override
    public void run() {
        System.out.println(&quot;\\nTask2 Start&quot;);
        for(int i = 201; i &amp;lt;= 299; i++) {
            System.out.print(i + &quot; &quot;);
        }
        System.out.println(&quot;\\nTask2 Finish&quot;);
    }
}

public class JoinThreadMain {
    public static void main(String[] args) {
        //Task1
        Task1 task1 = new Task1();
        task1.start();

        //Task2
        Task2 task2 = new Task2();
        Thread task2Thread = new Thread(task2);
        task2Thread.start();

        //Task3
        for(int i = 301; i &amp;lt;= 399; i++) {
            System.out.print(i + &quot; &quot;);
        }
        System.out.println(&quot;\\nTask3 Finish&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 각각의 스레드에서 별도로 Task1~2가 수행되는 코드입니다. 그렇기 때문에 각각의 Task의 수행이 마치는 것을 기다리지 않고 병렬로 실행되게 됩니다. 근데 여기서 Task1의 수행을 먼저 마치고 Task2, Task3의 코드를 처리해야 하거나, Task2의 실행 결과값을 Task1,3에서 사용해야 할 때는 어떻게 해야 할까요?&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Join() 메소드 예시&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;class Task1 extends Thread {
    @Override
    public void run() {
        System.out.println(&quot;\\nTask1 Start&quot;);
        for(int i = 101; i &amp;lt;= 199; i++) {
            System.out.print(i + &quot; &quot;);
        }
        System.out.println(&quot;\\nTask1 Finish&quot;);
    }
}

class Task2 implements Runnable {
    @Override
    public void run() {
        System.out.println(&quot;\\nTask2 Start&quot;);
        for(int i = 201; i &amp;lt;= 299; i++) {
            System.out.print(i + &quot; &quot;);
        }
        System.out.println(&quot;\\nTask2 Finish&quot;);
    }
}

public class JoinThreadMain {
    public static void main(String[] args) throws InterruptedException {
        //Task1
        Task1 task1 = new Task1();
        task1.start();

        //Task2
        Task2 task2 = new Task2();
        Thread task2Thread = new Thread(task2);
        task2Thread.start();

        // task2의 실행이 종료 될 때까지 Task3를 실행하지 않고 대기함
        task2Thread.join();
				//task1.join();

        //Task3
        for(int i = 301; i &amp;lt;= 399; i++) {
            System.out.print(i + &quot; &quot;);
        }
        System.out.println(&quot;\\nTask3 Finish&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Task1 Start
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 
Task2 Start
136 137 201 138 202 139 203 140 204 141 205 142 206 143 207 144 145 146 208 147 209 148 210 149 211 150 212 151 213 152 214 153 215 154 216 155 217 156 218 157 219 158 220 159 221 160 222 161 223 162 224 163 225 164 226 227 228 229 230 231 232 233 234 235 236 165 237 166 238 167 239 168 240 169 241 170 242 243 244 245 246 247 171 248 172 249 173 250 174 251 175 252 176 253 177 254 178 255 179 256 180 257 181 258 182 259 183 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 184 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 185 
Task2 Finish
186 187 188 301 189 302 190 303 191 304 305 192 306 193 194 195 196 307 197 308 198 309 199 310 
Task1 Finish
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 
Task3 Finish
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시와 같이 task2Thread.join(); 뒤에 .join()을 붙여주고 InterruptedException에 대한 예외 처리 부분을 추가해주면 됩니다. 그러면 Task2의 수행이 마치기 전까지는 Task3가 실행이 안되는 것을 확인할 수 있습니다. Task1도 적용하려면 마찬가지로 task1.join();를 추가해주면 task1과 task2가 병렬 실행되고 코드 수행이 종료되면 Task3가 실행되는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Thread.sleep()&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 메소드에 해당되며, Thread.sleep(밀리세컨드) 형식으로 작성해주고 InterruptedException 에 대한 예외 처리를 해주면 됩니다. 예를 들어 Thread.sleep(10000)이라고 적으면, 10초동안 해당 스레드는 대기 상태가 됩니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Task1ThreadMain {
    public static void main(String[] args) throws InterruptedException {
        Task1 task1 = new Task1();
        task1.start();

        for(int i = 201; i &amp;lt;= 299; i++) {
            System.out.print(i + &quot; &quot;);
        }
        System.out.println(&quot;\\nTask2 Finish&quot;);

				// 10초 동안 대기
        Thread.sleep(10000);

        for(int i = 301; i &amp;lt;= 399; i++) {
            System.out.print(i + &quot; &quot;);
        }
        System.out.println(&quot;\\nTask3 Finish&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Thread.yield()&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thread.sleep() 메서드와 비슷한 기능을 합니다. 다량의 CPU가 주어졌는데 이 CPU를 원하지 않는 경우라고 할때. yield()를 선언하면, 현재의 thread가 현재의 이용 가능한 이 상태를 양보하거나 양도할 의사가 있지만 가능한 빨리 다시 예약 되기를 원하다는 것을 스케쥴러에 알립니다. 당연하게도 스케줄러는(한정된 메모리를 여러 프로세스가 효율적으로 사용할 수 있도록 실행할 프로세스를 선택하는것) 이러한 정보를 무시할 수 있으며 운영 체제에 따라 다양한 동작을 하게됩니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;class Task1 extends Thread {
    @Override
    public void run() {
        System.out.println(&quot;\\nTask1 Start&quot;);
        for(int i = 101; i &amp;lt;= 199; i++) {
            System.out.print(i + &quot; &quot;);
        }
        Thread.yield();
        System.out.println(&quot;\\nTask1 Finish&quot;);
    }
}

public class Task1ThreadMain {
    public static void main(String[] args) {
        Task1 task1 = new Task1();
        task1.start();

        for(int i = 201; i &amp;lt;= 299; i++) {
            System.out.print(i + &quot; &quot;);
        }
        System.out.println(&quot;\\nTask2 Finish&quot;);

        for(int i = 301; i &amp;lt;= 399; i++) {
            System.out.print(i + &quot; &quot;);
        }
        System.out.println(&quot;\\nTask3 Finish&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Thread에서 Task1은 스케줄러에게 &amp;lsquo;충분하게 주목받았고, CPU가 충분하게 있으니, CPU를 포기하겠다&amp;rsquo;라고 말합니다. 하지만 이에 대해 스케줄러가 다음과 같이 답할 수 있습니다. &amp;lsquo;아니야, CPU를 가지고 있어야 하니 양도하지 말아줘' 라고 답할 수도 있습니다. 그래서 이게 그저 정보에 해당한다고 앞에서 말했던것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;공식 문서에 따르면 yield()를 사용할 필요가 거의 없으므로 목적이 명확하지 않으면 피해야 합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼에도 불구하고 yield()는 동시성 제어 구조 설계, 컴퓨팅 집약적인 프로그램에서 시스템 응답성 개선 등에 사용됩니다. 그러나 원하는 결과를 보장하기 위해서 상세한 프로파일링 및 벤치마킹이 수반되어야 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. synchronized&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 정적 메서드 중에서 Thread를 다룰 때 매우 중요한 키워드가 하나 있습니다. 바로 동기화 작업을 의미하는 synchronized 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HashTable에서도 사용되고있는데, HashTable은 자바 1에서부터 사용 가능하고, 이런 식으로 동기화에 접근하는 방법 또한 자바1에서부터 가능해 왔다는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;535&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NyNg1/btrN3mG96vJ/QgElGD3pZsKkydqSYbomE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NyNg1/btrN3mG96vJ/QgElGD3pZsKkydqSYbomE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NyNg1/btrN3mG96vJ/QgElGD3pZsKkydqSYbomE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNyNg1%2FbtrN3mG96vJ%2FQgElGD3pZsKkydqSYbomE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;777&quot; height=&quot;535&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;535&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HashTabel 내부의 코드를 보면, 대부분의 메소드가 동기화(synchronized) 키워드를 가지고 있다는 것을 확인할 수 있습니다. 그러므로 HashTable에서의 거의 모든 메소드는 동기화된 키워드, 즉 synchronized keyword를 가집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 여러분들은 지금 왜 HashTable이 동기화되어야 하는 건지에 대한 의문을 가지고 있으리라 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아 두 가지의 Thread 사이에서 공유되는 데이터가 존재한다고 가정했을때, 여기서는 HashTblae을 공유 하고 있다고 하겠습니다. 여기에 synchronized를 적어주면, Task1과 Task2 둘 중 하나의 Thread만 동기화된 메소드 안의 코드를 실행할 수 있죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어, 만약 특정 시점에서 동기화된 메서드가 50개 있다고 해보겠습니다. 그리고 이 중 하나의 Thread만 이 동기화된 50개의 메소드 중 아무 메소드를 실행시킬 수 있습니다. 50개의 동기화가 끝난 메서드가 있습니다. 그리고 여기서 동기화된 코드가 1000줄이 있고, 이 중 단 한개의 Thread만이 이 1000줄의 코드를 실행할 수 있습니다. 만약 이미 하나의 Thread가 1000줄의 코드 중 어떤 코드이든지 실행하고 있다면, 다른 Thread는 첫 번째 Thread가 동기화 코드에 대한 실행이 완료될 때까지 대기하다가 메소드를 실행하기 위한 접근 권한을 얻기 전에 나와주어야 합니다. 이게 sysnchronized keyworkd의 중요성이라고 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기화에서 이해하고 넘어가야 하는 정말 중요한 것은 동기화가 overhaed가 많이 생긴다는 점입니다. 이유는 이 1000줄의 코드가 단 한개의 Thread에 대해서만 실행이 가능하므로 다른 코드를 실행해야 하는 Thread 들은 무조건 대기를 해야 하기 때문입니다. 그러므로 시스템의 작동에 영향이 생길 확률이 높습니다. 자바의 최신 버전에서는 Thread를 더 안정성 있게 사용이 가능하도록 하는 대안들이 많이 있습니다. 동기화를 사용해 접근하는 방법에만 의존하지 않는다는 이야기 입니다. concurrent Collection이 그에 대한 예로 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.baeldung.com/java-thread-yield&quot;&gt;https://www.baeldung.com/java-thread-yield&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.udemy.com/course/best-java-programming/learn/lecture/29498884&quot;&gt;https://www.udemy.com/course/best-java-programming/learn/lecture/29498884&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>[JAVA]</category>
      <author>handr95</author>
      <guid isPermaLink="true">https://handr95.tistory.com/38</guid>
      <comments>https://handr95.tistory.com/38#entry38comment</comments>
      <pubDate>Sun, 9 Oct 2022 17:47:49 +0900</pubDate>
    </item>
    <item>
      <title>[Java] 자바 스레드와 동시성(3) - 우선 순위</title>
      <link>https://handr95.tistory.com/37</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;스레드 우선순위(Thread Priority)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 이미 알고 있듯이 Java는 스레드 스케줄러가 스레드의 우선 순위에 따라 스레드에 프로세서를 할당하는 멀티스레딩 환경 내에서 완전히 객체 지향적으로 작동합니다. Java에서 스레드를 생성할 때마다 항상 우선 순위가 할당됩니다. 우선순위는 스레드를 생성하는 동안 JVM에서 제공하거나 프로그래머가 명시적으로 제공할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드의 우선 순위는 각 스레드가 우선 순위를 갖는 개념으로, 조금 더 쉽게 설명 드린다면 모든 객체가 1에서 10 사이의 숫자로 표시되는 우선 순위를 갖는다고 말할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본 우선 순위는 예외로 5로 설정됩니다.&lt;/li&gt;
&lt;li&gt;최소 우선 순위는 1로 설정됩니다.&lt;/li&gt;
&lt;li&gt;최대 우선 순위는 10으로 설정됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 설정과 관련된 값들은 Thread.class 에 아래와 같이 상수값이 정의 되어있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;public static int NORM_PRIORITY&lt;/li&gt;
&lt;li&gt;public static int MIN_PRIORITY&lt;/li&gt;
&lt;li&gt;public static int MAX_PRIORITY&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&amp;nbsp;  스레드에 대해 허용되는 우선 순위 값은 1에서 10 사이입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java에서 스레드의 우선 순위를 가져오고 설정하는 메서드는 아래와 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;public final int getPriority()&lt;/b&gt; : 주어진 스레드의 우선 순위를 반환합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;public final void setPriority(int newPriority)&lt;/b&gt; : 스레드의 우선 순위를 newPriority 값으로 변경합니다. 이 메소드는 newPriority 매개변수의 값이 minimum(1) 및 maximum(10) 제한을 초과하는 경우 IllegalArgumentException을 발생시킵니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;우선 순위를 설정하는 예제&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;class PriorityThreadMain extends Thread {

    public void run() {
        System.out.println(&quot;Inside run method&quot;);
    }

    public static void main(String[] args) {
        PriorityThreadMain t1 = new PriorityThreadMain();
        PriorityThreadMain t2 = new PriorityThreadMain();
        PriorityThreadMain t3 = new PriorityThreadMain();

        // Thread 1 우선순위 5
        System.out.println(&quot;t1 thread priority : &quot; + t1.getPriority());

        // Thread 2 우선순위 5
        System.out.println(&quot;t2 thread priority : &quot; + t2.getPriority());

        // Thread 3 우선순위 5
        System.out.println(&quot;t3 thread priority : &quot; + t3.getPriority());

        // Setting priorities of above threads by
        // passing integer arguments
        t1.setPriority(2);
        t2.setPriority(5);
        t3.setPriority(8);
        // t3.setPriority(21); 같이 1~10 사이로 지정하지 않게되면 throw IllegalArgumentException 발생된다

        // 2
        System.out.println(&quot;t1 thread priority : &quot;+ t1.getPriority());

        // 5
        System.out.println(&quot;t2 thread priority : &quot;+ t2.getPriority());

        // 8
        System.out.println(&quot;t3 thread priority : &quot;+ t3.getPriority());

        // Main thread
        // 현재 실행중인 스레드 이름 반환
        System.out.println(&quot;Currently Executing Thread : &quot;+ Thread.currentThread().getName());

        System.out.println(&quot;Main thread priority : &quot;+ Thread.currentThread().getPriority());

        // 메인 스레드의 우선 순위를 10으로 세팅
        Thread.currentThread().setPriority(10);

        System.out.println(&quot;Main thread priority : &quot;+ Thread.currentThread().getPriority());
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;t1 thread priority : 5
t2 thread priority : 5
t3 thread priority : 5
t1 thread priority : 2
t2 thread priority : 5
t3 thread priority : 8
Currently Executing Thread : main
Main thread priority : 5
Main thread priority : 10
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;우선 순위가 가장 높은 스레드가 다른 스레드보다 먼저 실행 기회를 얻습니다. 우선 순위가 4, 6 및 1인 3개의 스레드 t1, t2 및 t3이 있다고 가정합니다. 따라서 스레드 t2는 최대 우선 순위 6을 기반으로 먼저 실행되고 그 후 t1이 실행되고 그 다음 t3이 실행됩니다.&lt;/li&gt;
&lt;li&gt;기본 스레드의 기본 우선 순위는 항상 5이며 나중에 변경할 수 있습니다. 다른 모든 스레드의 기본 우선 순위는 상위 스레드의 우선 순위에 따라 다릅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 우선순위를 설정만 해주었는데요, 아래 예제에서 우선 순위를 세팅하고 실행하여 지정한 우선 순위가 동작이 되는지 확인해보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;우선 순위를 설정하고 실제로 실행해본 예제&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;class Task1 extends Thread {
    @Override
    public void run() {
        System.out.println(&quot;\\nTask1 Start&quot;);
        for(int i = 101; i &amp;lt;= 199; i++) {
            System.out.print(i + &quot; &quot;);
        }
        System.out.println(&quot;\\nTask1 Finish&quot;);
    }
}

class Task2 implements Runnable {
    @Override
    public void run() {
        System.out.println(&quot;\\nTask2 Start&quot;);
        for(int i = 201; i &amp;lt;= 299; i++) {
            System.out.print(i + &quot; &quot;);
        }
        System.out.println(&quot;\\nTask2 Finish&quot;);
    }
}

public class Task2ThreadMain {
    public static void main(String[] args) {
        Task1 task1 = new Task1();
        task1.start();

        Task2 task2 = new Task2();
        Thread task2Thread = new Thread(task2);
        // task2 우선 순위를 가장 높게 설정
        task1.setPriority(Thread.MAX_PRIORITY);
        task2Thread.start();

        for(int i = 301; i &amp;lt;= 399; i++) {
            System.out.print(i + &quot; &quot;);
        }
        System.out.println(&quot;\\nTask3 Finish&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Task1 Start
101 102 103 104 105 106 107 108 109 110 111 112 301 113 302 
Task2 Start
114 201 303 202 115 203 204 304 205 305 116 306 206 307 117 308 207 309 118 310 311 208 312 119 313 314 209 315 210 120 211 212 316 213 121 214 215 317 216 122 217 318 218 123 219 319 220 124 221 320 222 125 223 321 224 126 225 322 226 127 227 323 228 128 229 324 230 129 231 325 232 130 233 326 234 131 235 327 236 132 237 328 238 133 239 329 240 134 241 330 242 135 243 331 244 136 245 332 246 137 247 333 248 138 249 334 250 139 251 335 252 140 253 336 254 141 255 256 337 257 142 258 259 260 338 261 143 262 263 339 264 144 265 266 267 268 269 270 340 271 145 272 273 341 274 146 275 276 342 277 147 278 343 279 148 280 281 282 283 344 284 149 285 286 287 288 289 290 291 292 293 345 294 150 295 296 297 298 299 346 
Task2 Finish
151 347 152 348 153 349 154 155 156 157 350 158 351 159 352 160 353 161 354 162 355 163 356 164 357 165 358 166 359 167 360 168 361 169 362 170 363 171 364 172 365 173 366 174 367 175 368 176 369 177 370 371 178 372 179 373 180 181 182 183 184 185 374 186 187 188 189 190 191 192 193 194 195 196 197 198 199 375 376 377 378 379 380 381 382 383 
Task1 Finish
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 
Task3 Finish
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행해보면 Task2가 Task1보다 먼저 수행이 종료되는 것을 확인 할 수 있습니다. 그러나 예제를 여러번 실행해본다면 Task1이나 Task3가 먼저 종료 될 때도 있는 것을 확인 할 수 있습니다. 여기서 주의해야 할 점은 우선 순위 설정은 단지 추천 순위 정도입니다. &lt;b&gt;우선 순위를 제일 높게 설정한다고 항상 먼저 실행됨을 보장 할 수는 없습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;참고
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.udemy.com/course/best-java-programming/learn/lecture/29498868#overview&quot;&gt;https://www.udemy.com/course/best-java-programming/learn/lecture/29498868#overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/java-thread-priority-multithreading&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.geeksforgeeks.org/java-thread-priority-multithreading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>[JAVA]</category>
      <category>java</category>
      <category>Thread</category>
      <category>thread priority</category>
      <author>handr95</author>
      <guid isPermaLink="true">https://handr95.tistory.com/37</guid>
      <comments>https://handr95.tistory.com/37#entry37comment</comments>
      <pubDate>Sat, 1 Oct 2022 21:45:23 +0900</pubDate>
    </item>
    <item>
      <title>[Java] 자바 스레드와 동시성(2) - 메인 스레드</title>
      <link>https://handr95.tistory.com/36</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Main Thread란&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java는 다중 스레드 프로그래밍 지원 기능이 내장되어있습니다. 다중 스레드 프로그램은 동시에 실행할 수 있는 두 개 이상의 스레드를 포함합니다. 각 스레드는 별도의 실행 경로를 정의합니다. Java 프로그램이 시작되면 하나의 스레드가 즉시 실행되기 시작합니다. 이것은 &lt;b&gt;프로그램이 시작될 때 실행&lt;/b&gt;되는 스레드이기 때문에 일반적으로 프로그램의 &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;메인 스레드(Main Thread)&lt;/span&gt;&lt;/b&gt;라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메인 스레드의 특성으로는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 스레드는 다른 &quot;Child&quot; 스레드를 생성 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;다양한 종료 작업을 수행하기 때문에 실행을 완료하는 마지막 스레드여야 하는 경우가 많습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흐름도(flow diagram)는 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHotfN/btrNol4onqn/AgdrQzsS7CgEmzchvigbIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHotfN/btrNol4onqn/AgdrQzsS7CgEmzchvigbIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHotfN/btrNol4onqn/AgdrQzsS7CgEmzchvigbIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHotfN%2FbtrNol4onqn%2FAgdrQzsS7CgEmzchvigbIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;634&quot; height=&quot;650&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;650&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 메인 스레드를 제어하는 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메인 스레드는 프로그램이 시작될 때 자동으로 생성됩니다. 그것을 제어하기 위해 우리는 메인 스레드의 참조를 얻어야 합니다. 이것은 Thread 클래스에 있는 &lt;b&gt;currentThread()&lt;/b&gt; 메서드를 호출하여 수행할 수 있습니다. 이 메서드는 &lt;b&gt;호출된 스레드에 대한 참조를 반환&lt;/b&gt;합니다. 기본 스레드의 기본 우선 순위는 5이며 나머지 모든 사용자 스레드의 우선 순위는 상위에서 하위로 상속됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;메인 스레드에서 생성한 자식 스레드가 우선 순위를 상속 받는 부분을 확인하는 예제&lt;/h3&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class MainThreadMain extends Thread {

    public static void main(String[] args) {

        // 현재 스레드 정보를 가져옴
        Thread t = Thread.currentThread();

        // 현재 스레드는 Main Thread
        System.out.println(&quot;Current thread: &quot;+ t.getName());

        // 메인 스레드의 이름을 변경함
        t.setName(&quot;Geeks&quot;);
        System.out.println(&quot;After name change: &quot;+ t.getName());

        // Main 스레드의 우선 순위 값을 가져옴
        System.out.println(&quot;Main thread priority: &quot;+ t.getPriority());

        // 우선순위를 가장 높게 설정
        t.setPriority(MAX_PRIORITY);

        // 변경된 우선 순위 확인
        System.out.println(&quot;Main thread new priority: &quot;+ t.getPriority());

        for (int i = 0; i &amp;lt; 5; i++) {
            System.out.println(&quot;Main thread&quot;);
        }

        // 메인 스레드에서 동작하는 자식 스레드를 생성
        Thread ct = new ChildThread();
        // 자식 스레드의 우선 순위를 가져옴
        // Main 스레드에 의해 생성됬으므로 Main 스레드의 우선 순위를 그대로 상속됩니다.
        System.out.println(&quot;Child thread priority: &quot;+ ct.getPriority());

        // 자식 스레드의 우선 순위를 변경함. 자식의 우선 순위를 변경한다고 Main 스레드의 우선 순위가 변경되지는 않음
        ct.setPriority(MIN_PRIORITY);

        System.out.println(&quot;Child thread new priority: &quot;+ ct.getPriority());

        // Starting child thread
        ct.start();
    }
}

class ChildThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i &amp;lt; 5; i++) {
            System.out.println(&quot;Child thread&quot;);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;Current thread: main
After name change: Geeks
Main thread priority: 5
Main thread new priority: 10
Main thread
Main thread
Main thread
Main thread
Main thread
Child thread priority: 10
Child thread new priority: 1
Child thread
Child thread
Child thread
Child thread
Child thread
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Java에서 main() 메서드와 메인 스레드 간의 관계에 대해 논의해 보겠습니다. 각 프로그램에 대해 JVM(Java Virtual Machine)에 의해 Main 스레드가 생성됩니다. &quot;Main&quot; 스레드는 먼저 main() 메서드의 존재를 확인한 다음 클래스를 초기화합니다. 참고로 JDK 6부터 main() 메서드는 독립 실행형 Java 응용 프로그램에서 필수 메서드 입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Main Thread 사용 시 교착 상태 확인(단일 스레드만)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메인 스레드를 사용하여, 즉 단일 스레드를 사용하여 교착 상태를 만들 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class DeadlockThreadMain {

    public static void main(String[] args) {
        try {
            System.out.println(&quot;Entering into Deadlock&quot;);
            
            Thread.currentThread().join();

            // join 메서드로 main 스레드가 종료 될때까지는 아해 메서드가 실행되지 않음
            System.out.println(&quot;This statement will never execute&quot;);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thread.currentThread().join() 문은 Main 스레드가 이 스레드(즉, 자체 대기)가 죽을 때까지 기다리라고 지시합니다. 따라서 메인 스레드는 자신이 죽을 때까지 기다립니다(Deadlock; 교착 상태).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;참고
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.udemy.com/course/best-java-programming/learn/lecture/29498868#overview&quot;&gt;https://www.udemy.com/course/best-java-programming/learn/lecture/29498868#overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/main-thread-java&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.geeksforgeeks.org/main-thread-java&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>[JAVA]</category>
      <category>java</category>
      <category>Thread</category>
      <author>handr95</author>
      <guid isPermaLink="true">https://handr95.tistory.com/36</guid>
      <comments>https://handr95.tistory.com/36#entry36comment</comments>
      <pubDate>Thu, 29 Sep 2022 22:24:50 +0900</pubDate>
    </item>
  </channel>
</rss>