<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>meun.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sun, 22 Dec 2024 00:15:38 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>meun.log</title>
            <url>https://velog.velcdn.com/images/be_have98/profile/adfaa61f-97bf-4b0d-9180-f481d53ff279/image.jfif</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. meun.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/be_have98" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Java] ThreadPool과 Executor 프레임워크(2)]]></title>
            <link>https://velog.io/@be_have98/Java-ThreadPool%EA%B3%BC-Executor-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC2</link>
            <guid>https://velog.io/@be_have98/Java-ThreadPool%EA%B3%BC-Executor-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC2</guid>
            <pubDate>Sun, 22 Dec 2024 00:15:38 GMT</pubDate>
            <description><![CDATA[<h1 id="우아한-종료-graceful-shutdown">우아한 종료, Graceful Shutdown</h1>
<p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/ExecutorService.html#isShutdown()"><code>ExecutorService</code></a>는 이미 진행중인 작업을 모두 완료한 다음 종료할 수 있도록 Graceful Shutdown을 아래 메소드로 지원한다.</p>
<h2 id="유관-메소드">유관 메소드</h2>
<h3 id="shutdown"><code>shutdown()</code></h3>
<pre><code class="language-java">void shutdown();</code></pre>
<ul>
<li>새로운 작업을 받지 않고, 이미 요청돤 작업을 모두 완료한 후 종료</li>
<li>Non-Blocking으로 동작<br/>

</li>
</ul>
<h3 id="shutdownnow"><code>shutdownNow()</code></h3>
<pre><code class="language-java">List&lt;Runnable&gt; shutdownNow();</code></pre>
<ul>
<li>실행 중인 작업 중단 및 대기 중인 작업 반환 후 즉시 종료</li>
<li>실행 중인 작업을 중단하기 위해 인터럽트 발생</li>
<li>Non-Blocking으로 동작<br/>

</li>
</ul>
<h3 id="isshutdown"><code>isShutdown()</code></h3>
<pre><code class="language-java">boolean isShutdown();</code></pre>
<ul>
<li>서비스 종료 여부 확인<br/>

</li>
</ul>
<h3 id="isterminated"><code>isTerminated()</code></h3>
<pre><code class="language-java">boolean isTerminated();</code></pre>
<ul>
<li><code>shutdown()</code>, <code>shutdownNow()</code> 호출 후 모든 작업이 완료되었는지 확인<br/>

</li>
</ul>
<h3 id="awaitterminationlong-timeout-timeunit-unit"><code>awaitTermination(long timeout, TimeUnit unit)</code></h3>
<pre><code class="language-java">boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;</code></pre>
<ul>
<li>서비스 종료 시 모든 작업이 완료되기를 지정된 시간동안 대기</li>
<li>Blocking 방식으로 동작<br/>

</li>
</ul>
<h3 id="close"><code>close()</code></h3>
<pre><code class="language-java">    @Override
    default void close() {
        boolean terminated = isTerminated();
        if (!terminated) {
            shutdown();
            boolean interrupted = false;
            while (!terminated) {
                try {
                    terminated = awaitTermination(1L, TimeUnit.DAYS);
                } catch (InterruptedException e) {
                    if (!interrupted) {
                        shutdownNow();
                        interrupted = true;
                    }
                }
            }
            if (interrupted) {
                Thread.currentThread().interrupt();
            }
        }
    }</code></pre>
<ul>
<li><code>shutdown()</code> 호출 후 작업 완료를 하루 동안 대기, 이후에도 완료되지 않으면 <code>shutdownNow()</code> 호출</li>
<li>호출한 Thread에 인터럽트가 발생한 경우에도 <code>shutdownNow()</code>가 호출됨</li>
<li>JDK 19부터 지원<br/>

</li>
</ul>
<br/>
<br/>

<h1 id="threadpool-관리-전략"><code>ThreadPool</code> 관리 전략</h1>
<p><code>Executors</code> 클래스에서는 <code>ThreadPool</code>을 효과적으로 관리하기 위한 여러 전략들을 제공한다.
이를 사용하거나 직접 정의할 수 있는데, 아래에서 상세히 알아본다.</p>
<h2 id="기본-제공">기본 제공</h2>
<h3 id="⭐newsinglethreadexecutor">⭐<code>newSingleThreadExecutor</code></h3>
<pre><code class="language-java">    public static ExecutorService newSingleThreadExecutor() {
        return newSingleThreadExecutor(defaultThreadFactory());
    }

    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new AutoShutdownDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue&lt;Runnable&gt;(),
                                    threadFactory));
    }</code></pre>
<ul>
<li><strong>단일 Thread를 사용하는 전략</strong></li>
<li>대기 큐의 크기는 무제한</li>
<li>작업 실행 중 장애로 인해 단일 스레드 종료 시 새로운 Thread가 생성됨</li>
<li>한 번에 두 개 이상의 작업이 활성화되지 않으며, 초과 Thread를 사용</li>
<li><code>newFixedThreadPool(1)</code>과 달리 초과 Thread를 사용하도록 재구성할 수 없음<br/>

</li>
</ul>
<h3 id="newsinglethreadscheduledexecutor"><code>newSingleThreadScheduledExecutor</code></h3>
<pre><code class="language-java">    public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
    }

    public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1, threadFactory));
    }</code></pre>
<ul>
<li>지정 시간동안 대기 후 실행하거나 주기적으로 실행하도록 명령 예약 가능한 단일 Thread를 사용하는 <code>Executor</code></li>
<li>이외 특징은 <code>newSingleThreadExecutor</code>와 동일<br/>


</li>
</ul>
<h3 id="newscheduledthreadpool"><code>newScheduledThreadPool</code></h3>
<pre><code class="language-java">    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

    public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }</code></pre>
<ul>
<li>지정 시간동안 대기 후 실행하거나 주기적으로 실행하도록 명령 예약 가능한 ThreadPool 생성<br/>

</li>
</ul>
<h3 id="⭐newfixedthreadpool">⭐<code>newFixedThreadPool</code></h3>
<pre><code class="language-java">    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue&lt;Runnable&gt;());
    }

    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue&lt;Runnable&gt;(),
                                      threadFactory);
    }</code></pre>
<ul>
<li>고정된 수의 Thread를 재사용하는 ThreadPool 생성</li>
<li>모든 Thread가 활성 상태일 때 추가 작업 요청 시 Thread를 사용할 수 있을때까지 대기 큐에서 대기</li>
<li>명시적으로 종료하기 전까지 Thread 유지</li>
<li>고정된 수의 Thread를 사용하므로 시스템 자원 사용량 예측 가능</li>
<li><strong>대기 큐의 크기는 무제한이므로 처리 속도보다 작업이 쌓이는 속도가 더 빠른 경우 문제 발생 가능</strong><br/>

</li>
</ul>
<h3 id="⭐newcachedthreadpool">⭐<code>newCachedThreadPool</code></h3>
<pre><code class="language-java">    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue&lt;Runnable&gt;());
    }

    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue&lt;Runnable&gt;(),
                                      threadFactory);
    }</code></pre>
<ul>
<li>필요에 따라 새 Thread를 생성하지만 이전에 생성된 Thread를 사용할 수 있을 때 재사용하는 ThreadPool 생성</li>
<li>일반적으로 수명이 짧은 비동기 작업을 많이 실행하는 프로그램의 성능 향상 가능</li>
<li>사용 가능한 기존 Thread가 없는 경우 새 Thread가 생성되어 ThreadPool에 추가되며, 60초동안 사용되지 않으면 Thread 종료 후 캐시에서 제거됨</li>
<li><code>SynchronousQueue</code>를 사용하여 별도 버퍼를 사용하지 않으며, 생산자가 소비자에게 직접 작업을 전달하고 반환하는 방식</li>
<li><strong>매우 유연한 전략이지만 시스템의 자원을 제한 없이 사용할 수 있어 주의가 필요함</strong><br/>

</li>
</ul>
<h3 id="newthreadpertaskexecutor"><code>newThreadPerTaskExecutor</code></h3>
<pre><code class="language-java">    public static ExecutorService newThreadPerTaskExecutor(ThreadFactory threadFactory) {
        return ThreadPerTaskExecutor.create(threadFactory);
    }</code></pre>
<ul>
<li>각 작업에 대해 새 Thread를 시작하는 <code>Executor</code> 생성</li>
<li>생성하는 Thread 수에 제한 없음</li>
<li><code>Future</code>에서 <code>cancel(true)</code> 호출 시 작업 실행 Thread 중단</li>
<li>JDK 21부터 지원<br/>

</li>
</ul>
<h3 id="newvirtualthreadpertaskexecutor"><code>newVirtualThreadPerTaskExecutor</code></h3>
<pre><code class="language-java">    public static ExecutorService newVirtualThreadPerTaskExecutor() {
        ThreadFactory factory = Thread.ofVirtual().factory();
        return newThreadPerTaskExecutor(factory);
    }</code></pre>
<ul>
<li>각 작업에 대해 새 Virtual Thread를 시작하는 <code>Executor</code> 생성</li>
<li>생성하는 Thread 수에 제한 없음</li>
<li>Virtual Thread를 생성하는 <code>ThreadFactory</code>를 사용하여 <code>newThreadPerTaskExecutor()</code>를 호출하는 방식</li>
<li>JDK 21부터 지원<br/>

</li>
</ul>
<h3 id="newworkstealingpool"><code>newWorkStealingPool</code></h3>
<pre><code class="language-java">    public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }</code></pre>
<ul>
<li>지정된 병렬 처리 수준을 지원하기 위해 충분한 Thread를 유지하는 ThreadPool 생성<ul>
<li>병렬 처리 수준은 작업 처리에 참여 중이거나 참여할 수 있는 최대 Thread 수에 해당</li>
</ul>
</li>
<li>경합을 줄이기 위해 여러 개의 대기 큐 사용 가능</li>
<li>실제 Thread 수는 동적으로 증가/감소</li>
<li>요청된 작업의 실행 순서를 보장하지 않음<br/>
<br/>

</li>
</ul>
<h2 id="사용자-정의">사용자 정의</h2>
<p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/ThreadPoolExecutor.html"><code>ThreadPoolExecutor</code></a>의 생성자를 통해 직접 수치를 조정하면서 <code>Executor</code>를 생성하는 방식이다.
적정 수치는 성능 테스트 등을 통해 검증할 수 있다.</p>
<pre><code class="language-java">    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue&lt;Runnable&gt; workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
                              ... 중략 ...
    }</code></pre>
<blockquote>
<p>단, <a href="https://velog.io/@be_have98/Java-ThreadPool%EA%B3%BC-Executor-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC1#threadpoolexecutor">1편에서 말한 것처럼</a> <code>maximumPoolSize</code>의 경우 <code>workQueue</code>가 모두 찬 상태에서 작업이 요청된 경우 <code>maximumPoolSize</code>까지 Thread가 생성된다는 의미이다.
<code>maximumPoolSize</code>를 의도한대로 사용하기 위해서는 <code>workQueue</code>의 사이즈가 제한되어 있는지 확인하는 것이 좋다.</p>
</blockquote>
<br/>
<br/>


<h1 id="threadfactory"><code>ThreadFactory</code></h1>
<p><code>Executors</code>에서 기본 제공되는 전략을 사용할 때 <code>ThreadFactory</code>를 지정하지 않는 경우 기본적으로 <code>DefaultThreadFactory</code>를 사용하도록 설정된다.</p>
<pre><code class="language-java">    public static ThreadFactory defaultThreadFactory() {
        return new DefaultThreadFactory();
    }</code></pre>
<p><code>DefaultThreadFactory</code>는 <code>ThreadFactory</code> 인터페이스를 구현하고 있다.</p>
<pre><code class="language-java">public interface ThreadFactory {

    Thread newThread(Runnable r);
}</code></pre>
<p>상세 구현은 아래와 같다.</p>
<pre><code class="language-java">    private static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        DefaultThreadFactory() {
            @SuppressWarnings(&quot;removal&quot;)
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = &quot;pool-&quot; +
                          poolNumber.getAndIncrement() +
                         &quot;-thread-&quot;;
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }</code></pre>
<br/>
<br/>

<h1 id="threadpoolexecutor-예외-정책"><code>ThreadPoolExecutor</code> 예외 정책</h1>
<p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/ThreadPoolExecutor.html"><code>ThreadPoolExecutor</code></a>의 생성자를 통해 대기 큐가 꽉 찼을 때 새로운 작업이 요청된 경우 처리 방안을 <code>RejectedExecutionHandler</code>를 통해 구현할 수 있다.</p>
<p><code>ThreadPoolExecutor</code>에는 기본 핸들러로 <code>AbortPolicy</code>가 등록되어 있다.</p>
<pre><code class="language-java">private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();</code></pre>
<h2 id="기본-제공-1">기본 제공</h2>
<h3 id="abortpolicy"><code>AbortPolicy</code></h3>
<pre><code class="language-java">    public static class AbortPolicy implements RejectedExecutionHandler {

        public AbortPolicy() { }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException(&quot;Task &quot; + r.toString() +
                                                 &quot; rejected from &quot; +
                                                 e.toString());
        }
    }</code></pre>
<ul>
<li><code>RejectedExecutionException</code> 예외를 발생시켜 새로운 요청 거절<br/>

</li>
</ul>
<h3 id="discardpolicy"><code>DiscardPolicy</code></h3>
<pre><code class="language-java">    public static class DiscardPolicy implements RejectedExecutionHandler {

        public DiscardPolicy() { }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }</code></pre>
<ul>
<li>새로운 작업 요청 시 무시</li>
</ul>
<h3 id="discardoldestpolicy"><code>DiscardOldestPolicy</code></h3>
<pre><code class="language-java">    public static class DiscardOldestPolicy implements RejectedExecutionHandler {

        public DiscardOldestPolicy() { }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
    }</code></pre>
<ul>
<li>새로운 작업 요청 시 가장 오래된 요청을 무시</li>
<li>다른 Thread가 작업이 종료되기를 기다리거나 작업 실패를 기록해야 하는 경우 유용하지 않음<br/>

</li>
</ul>
<h3 id="callerrunspolicy"><code>CallerRunsPolicy</code></h3>
<pre><code class="language-java">    public static class CallerRunsPolicy implements RejectedExecutionHandler {

        public CallerRunsPolicy() { }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }</code></pre>
<ul>
<li>새로운 작업을 요청한 Thread가 직접 작업 실행<br/>
<br/>

</li>
</ul>
<h2 id="사용자-정의-1">사용자 정의</h2>
<p><code>RejectedExecutionHandler</code>를 직접 구현하여 필요한 기능을 구현한다.</p>
<pre><code class="language-java">public interface RejectedExecutionHandler {

    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}</code></pre>
<br/>
<br/>


<h1 id="참고-자료">참고 자료</h1>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/ExecutorService.html">https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/ExecutorService.html</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/Executors.html">https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/Executors.html</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] ThreadPool과 Executor 프레임워크(1)]]></title>
            <link>https://velog.io/@be_have98/Java-ThreadPool%EA%B3%BC-Executor-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC1</link>
            <guid>https://velog.io/@be_have98/Java-ThreadPool%EA%B3%BC-Executor-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC1</guid>
            <pubDate>Sun, 15 Dec 2024 07:36:44 GMT</pubDate>
            <description><![CDATA[<h1 id="runnable의-한계"><code>Runnable</code>의 한계</h1>
<ul>
<li>반환 값이 없음</li>
<li>체크 예외 <code>throw</code> 불가</li>
</ul>
<br/>
<br/>

<h1 id="thread를-직접-다루게-되면"><code>Thread</code>를 직접 다루게 되면..</h1>
<p>아래 문제들을 겪게 된다.</p>
<ol>
<li>Thread 생성 시간으로 인한 성능 문제</li>
<li>Thread 관리 필요</li>
<li><code>Runnable</code> 인터페이스의 한계</li>
</ol>
<br/>

<p>이러한 문제들을 해결하기 위해 생성한 Thread를 재사용하는 자바에서는 <code>java.util.concurrent</code> 패키지를 통해 <code>Executor</code> 프레임워크를 제공한다.</p>
<p>이를 이용하면 매우 편리하게 Thread를 사용할 수 있게 된다.</p>
<br/>
<br/>

<h1 id="executor-프레임워크"><code>Executor</code> 프레임워크</h1>
<h2 id="개요">개요</h2>
<p>멀티 스레딩 및 병렬 처리를 쉽게 사용할 수 있도록 돕는 기능의 모음
<br/></p>
<h2 id="주요-구성-요소">주요 구성 요소</h2>
<h3 id="executor"><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/Executor.html"><code>Executor</code></a></h3>
<p><code>Runnable</code> 작업을 제출하고 반환 값을 받지 않는 <code>execute(Runnable command)</code>가 정의되어 있다.</p>
<pre><code class="language-java">public interface Executor {

    void execute(Runnable command);
}</code></pre>
<br/>

<h3 id="executorservice"><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/ExecutorService.html"><code>ExecutorService</code></a></h3>
<p><code>Executor</code> 인터페이스를 확장하여 작업 진행과 제어 기능을 추가로 제공한다.</p>
<pre><code class="language-java">public interface ExecutorService extends Executor, AutoCloseable {

    void shutdown();

    List&lt;Runnable&gt; shutdownNow();

    boolean isShutdown();

    boolean isTerminated();

    boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;

    &lt;T&gt; Future&lt;T&gt; submit(Callable&lt;T&gt; task);

    Future&lt;?&gt; submit(Runnable task);

    &lt;T&gt; List&lt;Future&lt;T&gt;&gt; invokeAll(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks) throws InterruptedException;

    &lt;T&gt; List&lt;Future&lt;T&gt;&gt; invokeAll(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks, long timeout, TimeUnit unit) throws InterruptedException;

    &lt;T&gt; T invokeAny(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks) throws InterruptedException, ExecutionException;

    @Override
    default void close() {
        boolean terminated = isTerminated();
        if (!terminated) {
            shutdown();
            boolean interrupted = false;
            while (!terminated) {
                try {
                    terminated = awaitTermination(1L, TimeUnit.DAYS);
                } catch (InterruptedException e) {
                    if (!interrupted) {
                        shutdownNow();
                        interrupted = true;
                    }
                }
            }
            if (interrupted) {
                Thread.currentThread().interrupt();
            }
        }
    }
}
</code></pre>
<br/>

<h4 id="주요-메서드">주요 메서드</h4>
<pre><code class="language-java">&lt;T&gt; Future&lt;T&gt; submit(Callable&lt;T&gt; task)</code></pre>
<ul>
<li><code>Callable</code> 작업을 제출하고 결과를 반환 받음<br/>

</li>
</ul>
<pre><code class="language-java">Future&lt;?&gt; submit(Runnable task);</code></pre>
<ul>
<li><code>Runnable</code> 작업을 제출하고 결과를 반환 받음</li>
<li><code>Runnable</code>은 반환 값이 없어 리턴 받은 <code>Future</code>를 통해 <code>get()</code> 수행 시 <code>null</code> 반환<br/>

</li>
</ul>
<pre><code class="language-java">&lt;T&gt; List&lt;Future&lt;T&gt;&gt; invokeAll(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks) throws InterruptedException;</code></pre>
<ul>
<li>모든 <code>Callable</code> 작업을 제출하고, 모든 작업이 완료될 때까지 대기</li>
<li>대기 시간을 지정하기 위해 <code>long timeout, TimeUnit unit</code>을 추가로 전달 받는 메서드도 오버로딩 되어 있음<br/>

</li>
</ul>
<pre><code class="language-java">&lt;T&gt; T invokeAny(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks) throws InterruptedException, ExecutionException;</code></pre>
<ul>
<li>하나의 <code>Callable</code> 작업이 완료될 때까지 기다리고 가장 먼저 완료된 작업의 결과를 반환, 완료되지 않은 나머지 작업은 모두 취소</li>
<li>대기 시간을 지정하기 위해 <code>long timeout, TimeUnit unit</code>을 추가로 전달 받는 메서드도 오버로딩 되어 있음</li>
</ul>
<br/>

<h3 id="threadpoolexecutor"><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/ThreadPoolExecutor.html"><code>ThreadPoolExecutor</code></a></h3>
<p><code>ExecutorService</code> 인터페이스의 대표적인 구현체다.
살펴보면 크게 <code>Thread Pool</code>과 <code>BlockingQueue</code>가 존재하며,
내부 클래스인 <code>Worker</code>를 정의 및 <code>Runnable</code>를 구현하여 멤버 변수로 가지고 있는 Thread의 작업을 수행하는 Worker로써 동작하도록 한다.</p>
<ul>
<li><code>Thread Pool</code> : <code>Thread</code> 관리 (=<code>workers</code>)</li>
<li><code>BlockingQueue</code> : <code>Thread</code>가 처리할 작업 보관 (=<code>workQueue</code>)</li>
</ul>
<p>생성자의 주요 매개변수로는 아래와 같다.</p>
<ul>
<li><code>corePoolSize</code> : Thread Pool에서 관리되는 기본 스레드의 수</li>
<li><code>maximumPoolSize</code> : Thread Pool에서 관리되는 최대 스레드의 수</li>
<li><code>keepAliveTime</code>, <code>TimeUnit unit</code> : 기본 스레드를 초과하여 만들어진 Thread가 생존 가능한 대기 시간이며, 이 시간 동안 처리할 작업이 없다면 초과 스레드는 제거</li>
<li><code>BlockingQueue</code> : 작업을 보관할 블로킹 큐</li>
</ul>
<pre><code class="language-java">public class ThreadPoolExecutor extends AbstractExecutorService {

    .. 중략 ..
    private final BlockingQueue&lt;Runnable&gt; workQueue;
    private final ReentrantLock mainLock = new ReentrantLock();
    private final HashSet&lt;Worker&gt; workers = new HashSet&lt;&gt;();
    private final Condition termination = mainLock.newCondition();
    private final SharedThreadContainer container;

    private int largestPoolSize;
    private long completedTaskCount;
    private volatile ThreadFactory threadFactory;
    private volatile RejectedExecutionHandler handler;
    private volatile long keepAliveTime;
    private volatile boolean allowCoreThreadTimeOut;
    private volatile int corePoolSize;
    private volatile int maximumPoolSize;
    private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();

    private final class Worker extends AbstractQueuedSynchronizer implements Runnable 

        @SuppressWarnings(&quot;serial&quot;) // Unlikely to be serializable
        final Thread thread;

        @SuppressWarnings(&quot;serial&quot;) // Not statically typed as Serializable
        Runnable firstTask;

        volatile long completedTasks;

        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

        public void run() {
            runWorker(this);
        }

        // Lock methods
        //
        // The value 0 represents the unlocked state.
        // The value 1 represents the locked state.

        protected boolean isHeldExclusively() {
            return getState() != 0;
        }

        protected boolean tryAcquire(int unused) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        protected boolean tryRelease(int unused) {
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        public void lock()        { acquire(1); }
        public boolean tryLock()  { return tryAcquire(1); }
        public void unlock()      { release(1); }
        public boolean isLocked() { return isHeldExclusively(); }

        void interruptIfStarted() {
            Thread t;
            if (getState() &gt;= 0 &amp;&amp; (t = thread) != null &amp;&amp; !t.isInterrupted()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
        }
    }

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue&lt;Runnable&gt; workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue&lt;Runnable&gt; workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize &lt; 0 ||
            maximumPoolSize &lt;= 0 ||
            maximumPoolSize &lt; corePoolSize ||
            keepAliveTime &lt; 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;

        String name = Objects.toIdentityString(this);
        this.container = SharedThreadContainer.create(name);
    }

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();

        int c = ctl.get();
        if (workerCountOf(c) &lt; corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) &amp;&amp; workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) &amp;&amp; remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }
    .. 중략 ..
}</code></pre>
<p>Thread Pool에 미리 지정한 수만큼 Thread를 생성하지 않으며, 
<code>execute(Runnable command)</code>를 통해 작업 시작 시 Worker의 수가 <code>corePoolSize</code>보다 적은 경우 Worker가 생성되도록 한다.</p>
<p><code>corePoolSize</code>보다 많은 Worker가 수행되고 있지만, <code>maximumPoolSize</code>보다 적은 수의 Worker가 수행되고 있는 경우 아래와 같이 동작한다.</p>
<ul>
<li>Queue가 가득 차지 않은 경우: 즉시 실행하지 않고 Queue에 작업 추가</li>
<li>Queue가 가득 찬 경우: <code>maxPoolSize</code>까지 Worker 생성하여 수행</li>
</ul>
<br/>

<h3 id="executors"><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/Executors.html"><code>Executors</code></a></h3>
<p><code>ThreadPoolExecutor</code> 객체 생성을 <code>static</code> 메서드로 아래와 같이 편리하게 사용할 수 있게 한다.</p>
<pre><code class="language-java">public class Executors {

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue&lt;Runnable&gt;());
    }

    ... 중략 ...
</code></pre>
<br/>


<h3 id="callable"><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/Callable.html"><code>Callable</code></a></h3>
<p>Generic을 통해 원하는 타입의 값을 반환할 수 있으며, <code>call()</code> 메서드 선언에 예외가 선언되어 있어 <code>Runnable</code>의 한계를 극복할 수 있게 한다.</p>
<pre><code class="language-java">public interface Callable&lt;V&gt; {

    V call() throws Exception;
}
</code></pre>
<br/>

<h3 id="future"><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/Future.html"><code>Future</code></a></h3>
<p><code>resultNow()</code>, <code>exceptionNow()</code>, <code>state()</code>의 경우 JDK 19 이상부터 사용 가능하다.</p>
<pre><code class="language-java">public interface Future&lt;V&gt; {

    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;

    /**
     * @since 19
     */
    default V resultNow() {
        if (!isDone())
            throw new IllegalStateException(&quot;Task has not completed&quot;);
        boolean interrupted = false;
        try {
            while (true) {
                try {
                    return get();
                } catch (InterruptedException e) {
                    interrupted = true;
                } catch (ExecutionException e) {
                    throw new IllegalStateException(&quot;Task completed with exception&quot;);
                } catch (CancellationException e) {
                    throw new IllegalStateException(&quot;Task was cancelled&quot;);
                }
            }
        } finally {
            if (interrupted) Thread.currentThread().interrupt();
        }
    }

    /**
     * @since 19
     */
    default Throwable exceptionNow() {
        if (!isDone())
            throw new IllegalStateException(&quot;Task has not completed&quot;);
        if (isCancelled())
            throw new IllegalStateException(&quot;Task was cancelled&quot;);
        boolean interrupted = false;
        try {
            while (true) {
                try {
                    get();
                    throw new IllegalStateException(&quot;Task completed with a result&quot;);
                } catch (InterruptedException e) {
                    interrupted = true;
                } catch (ExecutionException e) {
                    return e.getCause();
                }
            }
        } finally {
            if (interrupted) Thread.currentThread().interrupt();
        }
    }

    /**
     * @since 19
     */
    enum State {
        /**
         * The task has not completed.
         */
        RUNNING,
        /**
         * The task completed with a result.
         * @see Future#resultNow()
         */
        SUCCESS,
        /**
         * The task completed with an exception.
         * @see Future#exceptionNow()
         */
        FAILED,
        /**
         * The task was cancelled.
         * @see #cancel(boolean)
         */
        CANCELLED
    }

    /**
     * @since 19
     */
    default State state() {
        if (!isDone())
            return State.RUNNING;
        if (isCancelled())
            return State.CANCELLED;
        boolean interrupted = false;
        try {
            while (true) {
                try {
                    get();  // may throw InterruptedException when done
                    return State.SUCCESS;
                } catch (InterruptedException e) {
                    interrupted = true;
                } catch (ExecutionException e) {
                    return State.FAILED;
                }
            }
        } finally {
            if (interrupted) Thread.currentThread().interrupt();
        }
    }
}
</code></pre>
<br/>

<h4 id="주요-메서드-1">주요 메서드</h4>
<pre><code class="language-java">boolean cancel(boolean mayInterruptIfRunning);</code></pre>
<ul>
<li>아직 완료되지 않은 작업을 취소</li>
<li><code>mayInterruptIfRunning</code>의 값에 따라 동작 상이<ul>
<li><code>true</code> : 취소 상태로 변경 후 작업이 실행중인 경우 인터럽트 발생시켜 중단</li>
<li><code>false</code> : 취소 상태로 변경, 실행중인 작업 미중단</li>
</ul>
</li>
<li>취소 상태의 <code>Future</code>의 <code>get()</code>을 호출하면 <code>CancellationException</code> 발생<br/>

</li>
</ul>
<pre><code class="language-java">boolean isCancelled();</code></pre>
<ul>
<li>취소 여부 리턴<br/>

</li>
</ul>
<pre><code class="language-java">boolean isDone();</code></pre>
<ul>
<li>작업 완료 여부 리턴 (완료/취소/예외 발생하여 중단된 경우 <code>true</code>)<br/>

</li>
</ul>
<pre><code class="language-java">V get() throws InterruptedException, ExecutionException;</code></pre>
<ul>
<li>작업이 완료될 때까지 대기 후 결과 값 반환</li>
<li>대기하는 과정에서 메서드 호출 Thread의 Blocking 발생<br/>

</li>
</ul>
<h4 id="사용-예제">사용 예제</h4>
<p>아래와 같이 <code>Callable</code>의 작업을 수행 요청하고, 값을 가져오는 데에 사용할 수 있다.</p>
<pre><code class="language-java"> interface ArchiveSearcher { 
     String search(String target); 
 }

 class App {

   ExecutorService executor = ...;
   ArchiveSearcher searcher = ...;

   void showSearch(String target) throws InterruptedException {
     Callable&lt;String&gt; task = () -&gt; searcher.search(target);
     Future&lt;String&gt; future = executor.submit(task);
     displayOtherThings(); // do other things while searching
     try {
       displayText(future.get()); // use future
     } catch (ExecutionException ex) { cleanup(); return; }
   }
 }</code></pre>
<br/>

<h3 id="futuretask"><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/FutureTask.html"><code>FutureTask</code></a></h3>
<p><code>Future</code>의 대표적인 구현체로 <code>RunnableFuture</code>를 구현하며, 내부적으로 수행해야 할 작업과 상태 및 반환 값을 가지고 있다.</p>
<pre><code class="language-java">public class FutureTask&lt;V&gt; implements RunnableFuture&lt;V&gt; {

    private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;

    private Callable&lt;V&gt; callable;
    private Object outcome;
    private volatile Thread runner;
    private volatile WaitNode waiters;


    ... 중략 ...

    @SuppressWarnings(&quot;unchecked&quot;)
    private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL)
            return (V)x;
        if (s &gt;= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }

    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s &lt;= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

    private int awaitDone(boolean timed, long nanos) throws InterruptedException {
        WaitNode q = null;
        boolean queued = false;
        for (;;) {
            int s = state;
            if (s &gt; COMPLETING) {
                if (q != null)
                    q.thread = null;
                return s;
            }
            else if (s == COMPLETING)
                Thread.yield();
            else if (Thread.interrupted()) {
                removeWaiter(q);
                throw new InterruptedException();
            }
            else if (q == null) {
                if (timed &amp;&amp; nanos &lt;= 0L)
                    return s;
                q = new WaitNode();
            }
            else if (!queued)
                queued = WAITERS.weakCompareAndSet(this, q.next = waiters, q);
            else if (timed) {
                final long parkNanos;
                if (startTime == 0L) { // first time
                    startTime = System.nanoTime();
                    if (startTime == 0L)
                        startTime = 1L;
                    parkNanos = nanos;
                } else {
                    long elapsed = System.nanoTime() - startTime;
                    if (elapsed &gt;= nanos) {
                        removeWaiter(q);
                        return state;
                    }
                    parkNanos = nanos - elapsed;
                }
                if (state &lt; COMPLETING)
                    LockSupport.parkNanos(this, parkNanos);
            }
            else
                LockSupport.park(this);
        }
    }
    ... 중략 ...
}
</code></pre>
<br/>
<br/>

<h1 id="future의-필요성"><code>Future</code>의 필요성</h1>
<p>왜 <code>ExecutorService</code>에서 바로 반환 값을 받지 않고 <code>Future</code>의 <code>get()</code>을 통하여 반환 값을 받을까?</p>
<p><code>ExecutorService</code>에서 작업 요청 후 결과 반환 시점까지 대기한다면 요청 Thread에서 Blocking이 발생하여 성능 저하 발생 가능하다.
<code>Future</code>는 요청 스레드를 Blocking 상태로 만들지 않도록 하고, <code>get()</code> 호출 전까지 필요한 작업을 진행할 수 있도록 한다.</p>
<br/>
<br/>


<h1 id="참고-자료">참고 자료</h1>
<ul>
<li>JDK 21 공식 문서 (각 클래스/인터페이스명에 링크)</li>
<li><a href="https://leeyh0216.github.io/posts/truth_of_threadpoolexecutor/">https://leeyh0216.github.io/posts/truth_of_threadpoolexecutor/</a></li>
</ul>
<br/>
<br/>


<p>2편에 계속...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 동시성 컬렉션]]></title>
            <link>https://velog.io/@be_have98/Java-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%BB%AC%EB%A0%89%EC%85%98</link>
            <guid>https://velog.io/@be_have98/Java-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%BB%AC%EB%A0%89%EC%85%98</guid>
            <pubDate>Sun, 01 Dec 2024 12:50:11 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<p><code>java.util</code> 패키지 내 컬렉션 프레임워크의 대부분은 <code>thread-safe</code> 하지 않다.
이러한 문제점을 자바에서는 어떻게 해결하였는지 살펴보자.
<br/>
<br/></p>
<h1 id="vector와-synchronized"><code>Vector</code>와 <code>synchronized</code></h1>
<p>최초에 자바는 이러한 문제를 해결하기 위해 JDK 1.0에서 <code>java.util</code> 패키지에서 <code>Vector</code> 클래스를 지원하였다.
하지만 아래 코드와 같이 <code>synchronized</code> 를 통해 동기화를 구현하여 단일 스레드에서도 불필요한 성능 저하가 발생하게 되었고, 지금은 거의 사용되지 않게 되었다.
현재는 하위 호환을 위해 존재하나, 사용을 권장하지 않는다.
<br/></p>
<pre><code class="language-java">package java.util;

public class Vector&lt;E&gt;
    extends AbstractList&lt;E&gt;
    implements List&lt;E&gt;, RandomAccess, Cloneable, java.io.Serializable
{

    @SuppressWarnings(&quot;serial&quot;) // Conditionally serializable
    protected Object[] elementData;

    protected int elementCount;
    protected int capacityIncrement;

    ... 중략 ...


    public synchronized void copyInto(Object[] anArray) {
        System.arraycopy(elementData, 0, anArray, 0, elementCount);
    }

    public synchronized int capacity() {
        return elementData.length;
    }

    public synchronized int size() {
        return elementCount;
    }

    public synchronized boolean isEmpty() {
        return elementCount == 0;
    }

    public synchronized int indexOf(Object o, int index) {
        if (o == null) {
            for (int i = index ; i &lt; elementCount ; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = index ; i &lt; elementCount ; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

    ... 중략 ...
}
</code></pre>
<br/>
<br/>

<h1 id="동시성-컬렉션">동시성 컬렉션</h1>
<h2 id="1-collectionssynchronizedxx">1) <code>Collections.synchronizedXX()</code></h2>
<p>프록시 패턴을 이용하여 <code>Collection</code> 타입 객체를 <code>synchronized</code> 블록 내에서 동작하도록 구현한 기능이다.</p>
<h3 id="유관-메서드">유관 메서드</h3>
<h4 id="synchronizedcollection"><code>synchronizedCollection()</code></h4>
<pre><code class="language-java">public static &lt;T&gt; Collection&lt;T&gt; synchronizedCollection(Collection&lt;T&gt; c) {
    return new SynchronizedCollection&lt;&gt;(c);
}</code></pre>
<h4 id="synchronizedlist"><code>synchronizedList()</code></h4>
<pre><code class="language-java">public static &lt;T&gt; List&lt;T&gt; synchronizedList(List&lt;T&gt; list) {
    return (list instanceof RandomAccess ?
        new SynchronizedRandomAccessList&lt;&gt;(list) :
        new SynchronizedList&lt;&gt;(list));
}</code></pre>
<h4 id="synchronizedmap"><code>synchronizedMap()</code></h4>
<pre><code class="language-java">public static &lt;K,V&gt; Map&lt;K,V&gt; synchronizedMap(Map&lt;K,V&gt; m) {
    return new SynchronizedMap&lt;&gt;(m);
}</code></pre>
<h4 id="synchronizedset"><code>synchronizedSet()</code></h4>
<pre><code class="language-java">public static &lt;T&gt; Set&lt;T&gt; synchronizedSet(Set&lt;T&gt; s) {
    return new SynchronizedSet&lt;&gt;(s);
}</code></pre>
<h4 id="synchronizednavigablemap"><code>synchronizedNavigableMap()</code></h4>
<pre><code class="language-java">public static &lt;K,V&gt; NavigableMap&lt;K,V&gt; synchronizedNavigableMap(NavigableMap&lt;K,V&gt; m) {
    return new SynchronizedNavigableMap&lt;&gt;(m);
}</code></pre>
<h4 id="synchronizednavigableset"><code>synchronizedNavigableSet()</code></h4>
<pre><code class="language-java">public static &lt;T&gt; NavigableSet&lt;T&gt; synchronizedNavigableSet(NavigableSet&lt;T&gt; s) {
    return new SynchronizedNavigableSet&lt;&gt;(s);
}</code></pre>
<h4 id="synchronizedsortedmap"><code>synchronizedSortedMap()</code></h4>
<pre><code class="language-java">public static &lt;K,V&gt; SortedMap&lt;K,V&gt; synchronizedSortedMap(SortedMap&lt;K,V&gt; m) {
    return new SynchronizedSortedMap&lt;&gt;(m);
}</code></pre>
<h4 id="synchronizedsortedset"><code>synchronizedSortedSet()</code></h4>
<pre><code class="language-java">public static &lt;T&gt; SortedSet&lt;T&gt; synchronizedSortedSet(SortedSet&lt;T&gt; s) {
    return new SynchronizedSortedSet&lt;&gt;(s);    
}</code></pre>
<blockquote>
<p><strong><code>NavigableMap</code>과 <code>NavigableSet</code>이란?</strong>
주어진 검색 대상에 가장 가까운 일치 항목을 반환하는 탐색 메서드를 제공하기 위해 <code>SortedMap</code>과 <code>SortedSet</code>을 확장하였으며, 각각 정렬된 맵과 집합을 지원한다. <br/> 두 인터페이스의 대표적인 구현체로는 <code>TreeMap</code>, <code>TreeSet</code>이 있다.</p>
</blockquote>
<br/>

<h3 id="프록시-패턴-이용-방식">프록시 패턴 이용 방식</h3>
<p><code>SynchronizedList</code>를 예시로 살펴보자.
구현체 내부에 <code>list</code> 멤버변수를 두어 생성자로 넘겨 받은 <code>List</code> 객체로 설정한다.</p>
<p>이후 <code>get()</code>, <code>set()</code>, <code>add()</code>, <code>remove()</code>와 같은 동작에서 <code>synchronized</code> 블록 내에서 기존 객체의 동작을 수행하도록 구현되어 있다.</p>
<p>편리하게 이용 가능하지만 동기화 오버헤드로 인한 성능 저하, 동기화 최적화가 불가하다는 한계가 존재한다.
<br/></p>
<pre><code class="language-java">static class SynchronizedList&lt;E&gt; extends SynchronizedCollection&lt;E&gt; implements List&lt;E&gt; {

    @SuppressWarnings(&quot;serial&quot;) // Conditionally serializable
    final List&lt;E&gt; list;

    SynchronizedList(List&lt;E&gt; list) {
        super(list);
        this.list = list;
    }
    SynchronizedList(List&lt;E&gt; list, Object mutex) {
        super(list, mutex);
        this.list = list;
    }

    public boolean equals(Object o) {
        if (this == o)
            return true;
        synchronized (mutex) {return list.equals(o);}
    }
    public int hashCode() {
        synchronized (mutex) {return list.hashCode();}
    }

    public E get(int index) {
        synchronized (mutex) {return list.get(index);}
    }
    public E set(int index, E element) {
        synchronized (mutex) {return list.set(index, element);}
    }
    public void add(int index, E element) {
        synchronized (mutex) {list.add(index, element);}
    }
    public E remove(int index) {
        synchronized (mutex) {return list.remove(index);}
    }

    public int indexOf(Object o) {
        synchronized (mutex) {return list.indexOf(o);}
    }
    public int lastIndexOf(Object o) {
        synchronized (mutex) {return list.lastIndexOf(o);}
    }

    public boolean addAll(int index, Collection&lt;? extends E&gt; c) {
        synchronized (mutex) {return list.addAll(index, c);}
    }

    ... 중략 ...
}</code></pre>
<br/>
<br/>

<h2 id="2-javautilconcurrent">2) <code>java.util.concurrent</code></h2>
<p><code>LinkedHashSet</code>, <code>LinkedHashMap</code>과 같이 입력 순서를 유지하는 동시에 <code>thread-safe</code>한 <code>Set</code>, <code>Map</code> 구현체는 제공하지 않는다.</p>
<h3 id="list"><code>List</code></h3>
<h4 id="copyonwritearraylist"><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/CopyOnWriteArrayList.html"><code>CopyOnWriteArrayList</code></a></h4>
<ul>
<li>모든 요소 변경과 관련된 연산(추가, 설정 등)이 기본 배열의 새 복사본을 만들어서 구현되는 <code>ArrayList</code>의 <code>thread-safe</code>한 버전</li>
<li>일반적으로 비용이 많이 들지만 순회 연산이 변경과 관련된 연산보다 훨씬 많은 경우 다른 대안보다 효율적<br/>

</li>
</ul>
<h3 id="set"><code>Set</code></h3>
<h4 id="copyonwritearrayset"><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/CopyOnWriteArraySet.html"><code>CopyOnWriteArraySet</code></a></h4>
<ul>
<li>모든 연산에 내부 <code>CopyOnWriteArrayList</code>를 사용하는 <code>Set</code></li>
<li>일반적으로 집합 크기가 작고, 읽기 전용 연산이 쓰기 연산보다 훨씬 많으며, 순회하는 동안 스레드 간 간섭을 방지해야 하는 애플리케이션에 가장 적합</li>
<li>요소 변경과 관련된 연산은 일반적으로 전체 기본 배열을 복사해야 하므로 비용이 많이 들어감</li>
</ul>
<h4 id="concurrentskiplistset"><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/ConcurrentSkipListSet.html"><code>ConcurrentSkipListSet</code></a></h4>
<ul>
<li><code>ConcurrentSkipListMap</code>에 기반한 확장 가능한 동시 탐색 가능한 구현체</li>
<li>집합의 요소는 사용된 생성자에 따라 정렬된 상태로 유지 (<code>Comparator</code> 사용 가능)<br/>

</li>
</ul>
<h3 id="map"><code>Map</code></h3>
<h4 id="concurrenthashmap"><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/ConcurrentHashMap.html"><code>ConcurrentHashMap</code></a></h4>
<ul>
<li>검색의 완전한 동시성과 업데이트에 대한 높은 기대 동시성을 지원하는 해시 테이블</li>
<li>해시테이블과 동일한 기능을 제공하며, 해시테이블의 각 메서드에 대응하는 메서드 버전을 포함</li>
<li>모든 연산이 <code>thread-safe</code> 하지만 검색 연산에는 락을 사용하지 않으며, 모든 액세스를 방지하는 방식으로 전체 테이블을 잠그는 기능은 지원되지 않음 </li>
</ul>
<h4 id="concurrentskiplistmap"><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/ConcurrentSkipListMap.html"><code>ConcurrentSkipListMap</code></a></h4>
<ul>
<li>확장 가능한 <code>ConcurrentNavigableMap</code>의 구현체</li>
<li>사용된 생성자에 따라 정렬된 상태로 유지 (<code>Comparator</code> 사용 가능)<br/>

</li>
</ul>
<h3 id="queue"><code>Queue</code></h3>
<h4 id="concurrentlinkedqueue"><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/ConcurrentLinkedQueue.html"><code>ConcurrentLinkedQueue</code></a></h4>
<ul>
<li>연결된 노드를 기반으로 하는 <code>thread-safe</code> 큐</li>
<li>요소를 FIFO 방식으로 정렬 (큐의 헤드는 큐에 가장 오래 대기 중인 요소)</li>
<li>효율적인 Non-Blocking 알고리즘을 사용<br/>

</li>
</ul>
<h3 id="deque"><code>Deque</code></h3>
<h4 id="concurrentlinkeddeque"><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/ConcurrentLinkedDeque.html"><code>ConcurrentLinkedDeque</code></a></h4>
<ul>
<li>연결된 노드를 기반으로 하는 <code>thread-safe</code> Deque</li>
<li>컬렉션의 비동기적 특성으로 인해 현재 요소 수를 확인하려면 요소를 순회해야 하므로 순회 중에 컬렉션이 수정되면 부정확한 결과가 리턴될 수 있음</li>
<li><code>addAll()</code>, <code>removeIf()</code> 또는 <code>forEach()</code>와 같이 여러 요소를 대량으로 추가, 제거 또는 조회하는 연산은 원자적으로 수행된다고 보장할 수 없음<br/>
<br/>


</li>
</ul>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li><a href="https://tech.socarcorp.kr/dev/2021/10/19/sub-interfaces-navigablemap.html">https://tech.socarcorp.kr/dev/2021/10/19/sub-interfaces-navigablemap.html</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/NavigableMap.html">https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/NavigableMap.html</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] AtomicInteger와 CAS 연산]]></title>
            <link>https://velog.io/@be_have98/Java-AtomicInteger%EC%99%80-CAS-%EC%97%B0%EC%82%B0</link>
            <guid>https://velog.io/@be_have98/Java-AtomicInteger%EC%99%80-CAS-%EC%97%B0%EC%82%B0</guid>
            <pubDate>Tue, 26 Nov 2024 21:10:48 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<p>보통 동시성 이슈의 많은 원인은 값을 조회하고 값을 변경하는 과정이 원자적이지 않기 때문에 발생한다.
자바에서 이러한 연산의 원자성을 지원하기 위해 <code>AtomicInteger</code>와 같은 클래스를 지원한다.
<br/>
<br/></p>
<h1 id="연산의-원자성">연산의 원자성</h1>
<p>해당 연산이 더 이상 나눌 수 없는 단위로 수행되는 경우 이를 <strong>원자적 연산</strong>이라 한다.
<code>i++</code>과 같은 연산도 <code>i = i + 1</code>의 축약이므로 원자적인 연산은 아니다.</p>
<p>멀티 스레드를 사용하는 경우 원자적 연산은 문제가 되지 않아 대체적으로 문제가 되는 연산은 원자적이지 않은 연산이다.
<br/>
<br/></p>
<h1 id="cas-연산">CAS 연산</h1>
<p>락을 사용하지 않고 원자적인 연산을 수행할 수 있게 하며, <strong>CAS(Compare-And-Swap, Compare-And-Set) 연산</strong> 또는 <strong>락 프리 기법</strong>이라고도 한다.
소프트웨어가 아닌 CPU 하드웨어에서 지원하는 기능이다.</p>
<h2 id="동작-방식-예시">동작 방식 예시</h2>
<ol>
<li>현재 변경하고자 하는 값이 예상한 값과 동일한지 비교</li>
<li>예상한 값과 동일한 경우 바꾸고자 하는 값으로 변경, 동일하지 않은 경우 변경하지 않음<br/>


</li>
</ol>
<h2 id="주요-라이브러리">주요 라이브러리</h2>
<p>CAS 연산을 사용하는 자바 라이브러리 중 일부이다.</p>
<ol>
<li><code>java.util.concurrent</code> 패키지<ul>
<li>자바 표준 라이브러리에서 제공하는 라이브러리로, <code>atomic</code> 패키지 내 클래스들은 CAS 연산을 기반으로 동작한다.</li>
<li>이외에 <code>ForkJoinPool</code>도 이 연산을 기반으로 동작한다.</li>
</ul>
</li>
<li><a href="https://github.com/netty/netty">Netty</a> : 비동기 이벤트 기반 네트워크 애플리케이션 프레임워크</li>
<li>Reactor, RxJava : 비동기 스트림 처리에 사용되며 이벤트 기반 라이브러리</li>
<li><a href="https://github.com/JCTools/JCTools">JCTools</a> : 경량화된 동시성 도구 모음</li>
<li><a href="https://github.com/LMAX-Exchange/disruptor">Disruptor</a> : 고성능 Non-Blocking 메시징 라이브러리로, CAS 연산 기반의 Ring Buffer를 사용한다.</li>
</ol>
<p>Spring Webflux를 사용하게 되면 Reactor와 Netty를 접하게 되는데, 해당 라이브러리 모두 CAS 연산을 사용한다는 점을 새롭게 알게 되었다.
<br/>
<br/></p>
<h1 id="atomicinteger-클래스"><code>AtomicInteger</code> 클래스</h1>
<p>멀티 스레드에서 안전한 연산을 지원하기 위한 자바의 클래스로 JDK 1.5부터 지원되었다.
내부적으로 <code>volatile</code> 타입의 <code>int</code> 변수를 가지고 있으며, 해당 변수로 연산을 진행한다.
숫자 기반 클래스를 다루는 도구와 유틸리티에서 동일하게 사용 가능하도록 <code>Number</code> 인터페이스를 확장했다.</p>
<pre><code class="language-java">public class AtomicInteger extends Number implements java.io.Serializable {

    private static final Unsafe U = Unsafe.getUnsafe();
    private static final long VALUE
        = U.objectFieldOffset(AtomicInteger.class, &quot;value&quot;);

    private volatile int value;

    /**
     * Creates a new AtomicInteger with the given initial value.
     *
     * @param initialValue the initial value
     */
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    /**
     * Creates a new AtomicInteger with initial value {@code 0}.
     */
    public AtomicInteger() {
    }

    public final int get() {
        return value;
    }

    public final void set(int newValue) {
        value = newValue;
    }

    public final boolean compareAndSet(int expectedValue, int newValue) {
        return U.compareAndSetInt(this, VALUE, expectedValue, newValue);
    }

    public final int incrementAndGet() {
        return U.getAndAddInt(this, VALUE, 1) + 1;
    }

    // 중략
}
</code></pre>
<br/>


<h2 id="주요-메소드">주요 메소드</h2>
<h3 id="get"><code>get()</code></h3>
<pre><code class="language-java">public final int get()</code></pre>
<ul>
<li>현재 값 조회<br/>

</li>
</ul>
<h3 id="set"><code>set()</code></h3>
<pre><code class="language-java">public final void set(int newValue)</code></pre>
<ul>
<li>전달 받은 <code>newValue</code>로 값을 세팅<br/>


</li>
</ul>
<h3 id="compareandset"><code>compareAndSet()</code></h3>
<pre><code class="language-java">public final boolean compareAndSet(int expectedValue, int newValue)</code></pre>
<ul>
<li>현재 값이 <code>expectedValue</code>와 일치하면 <code>newValue</code>로 변경<br/>


</li>
</ul>
<h3 id="incrementandget"><code>incrementAndGet()</code></h3>
<pre><code class="language-java">public final int incrementAndGet()</code></pre>
<ul>
<li>현재 값 증가</li>
</ul>
<h3 id="decrementandget"><code>decrementAndGet()</code></h3>
<pre><code class="language-java">public final int decrementAndGet()</code></pre>
<ul>
<li>현재 값 감소</li>
</ul>
<br/>

<h2 id="유관-클래스">유관 클래스</h2>
<p><code>Intger</code>의 원자적 연산을 지원하는 <code>AtmoicInteger</code> 외에도 각 자료형별로 아래와 같이 클래스가 존재한다.
<img src="https://velog.velcdn.com/images/be_have98/post/25eb24f6-fa11-4b43-b7bd-a3516901752c/image.png" alt=""></p>
<br/>
<br/>



<h1 id="참고-자료">참고 자료</h1>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/atomic/AtomicInteger.html">https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/atomic/AtomicInteger.html</a></li>
<li><a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.7">https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.7</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 생산자-소비자 문제와 BlockingQueue]]></title>
            <link>https://velog.io/@be_have98/Java-%EC%83%9D%EC%82%B0%EC%9E%90-%EC%86%8C%EB%B9%84%EC%9E%90-%EB%AC%B8%EC%A0%9C%EC%99%80-BlockingQueue</link>
            <guid>https://velog.io/@be_have98/Java-%EC%83%9D%EC%82%B0%EC%9E%90-%EC%86%8C%EB%B9%84%EC%9E%90-%EB%AC%B8%EC%A0%9C%EC%99%80-BlockingQueue</guid>
            <pubDate>Tue, 19 Nov 2024 21:24:30 GMT</pubDate>
            <description><![CDATA[<h1 id="이전-글">이전 글</h1>
<p><a href="https://velog.io/@be_have98/Java-%EC%83%9D%EC%82%B0%EC%9E%90-%EC%86%8C%EB%B9%84%EC%9E%90-%EB%AC%B8%EC%A0%9C%EC%99%80-Object-%ED%81%B4%EB%9E%98%EC%8A%A4">[Java] 생산자-소비자 문제와 Object 클래스</a></p>
<br/>
<br/>


<h1 id="개요">개요</h1>
<p>이전에는 <code>Object</code> 클래스를 통해 생산자-소비자 문제를 해결하려 하였으나 아래 문제점이 존재했었다.</p>
<ul>
<li><strong>비효율</strong> : 특정 Thread를 깨울 수 없어 동일 타입의 Thread를 깨울 때 비효율 발생 가능</li>
<li><strong>기아 상태</strong> : 특정 Thread를 깨울 수 없어 특정 스레드를 제외하고 깨울 수 있는 상황 발생 가능</li>
</ul>
<p>이번에는 또 다른 해결 방법인 <code>Condition</code>과 <code>BlockingQueue</code>를 알아본다.
<br/>
<br/></p>
<h1 id="condition-인터페이스"><code>Condition</code> 인터페이스</h1>
<p><code>Object</code>의 모니터 락 관련 메서드를 별도의 객체로 분리하여 <code>Lock</code> 구현체와 함께 사용 가능하도록 함으로써 객체당 여러 개의 대기 집합을 갖도록 한다.
<code>Lock</code> 인터페이스의 <code>newCondition()</code> 통하여 생성 가능하며, 생성 시 해당 Lock의 스레드 대기 공간을 생성할 수 있다.
<code>Condition</code>을 생산자와 소비자 각각 생성함으로써 특정 타입의 Thread를 깨울 수 있도록 처리할 수 있다.</p>
<pre><code class="language-java">public interface Condition {

    void await() throws InterruptedException;
    void awaitUninterruptibly();
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    boolean awaitUntil(Date deadline) throws InterruptedException;
    void signal();
    void signalAll();
}</code></pre>
<br/>


<h2 id="주요-메서드">주요 메서드</h2>
<h3 id="await"><code>await()</code></h3>
<pre><code class="language-java">void await() throws InterruptedException</code></pre>
<ul>
<li>현재 스레드를 <code>WAITING</code> 상태로 <code>condition</code>에 보관</li>
<li>호출 시 인터럽트 플래그가 설정되어 있거나 대기 중 인터럽트 발생 시 <code>InterruptedException</code> 발생하고 인터럽트 플래그가 지워짐</li>
<li><code>Object.wait()</code>과 유사<br/>



</li>
</ul>
<h3 id="signal"><code>signal()</code></h3>
<pre><code class="language-java">void signal()</code></pre>
<ul>
<li><code>condition</code>에서 대기중인 스레드 중 하나를 깨움</li>
<li><code>Object.notify()</code>와 유사<br/>


</li>
</ul>
<h2 id="예제">예제</h2>
<pre><code class="language-java"> class BoundedBuffer&lt;E&gt; {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition(); // 생산자 Thread 대기
   final Condition notEmpty = lock.newCondition(); // 소비자 Thread 대기

   final Object[] items = new Object[100];
   int putptr, takeptr, count;

   public void put(E x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length)
         notFull.await(); // 생산자 Thread 대기
       items[putptr] = x;
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal(); // 소비자 Thread 깨움
     } finally {
       lock.unlock();
     }
   }

   public E take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0)
         notEmpty.await(); // 소비자 Thread 대기
       E x = (E) items[takeptr];
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal(); // 생산자 Thread 깨움
       return x;
     } finally {
       lock.unlock();
     }
   }
 }</code></pre>
<br/>
<br/>


<h1 id="blockingqueue-인터페이스"><code>BlockingQueue</code> 인터페이스</h1>
<p>일반적인 <code>Queue</code> 대비 아래 기능을 추가로 지원하는 큐이다.</p>
<ul>
<li>요소 검색 시 큐가 비어 있지 않을 때까지 기다리는 작업</li>
<li>요소 저장 시 큐에 공간이 확보될 때까지 기다리는 작업</li>
</ul>
<pre><code class="language-java">
public interface BlockingQueue&lt;E&gt; extends Queue&lt;E&gt; {

    boolean add(E e);
    boolean offer(E e);
    void put(E e) throws InterruptedException;
    boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException;
    E take() throws InterruptedException;
    E poll(long timeout, TimeUnit unit) throws InterruptedException;
    int remainingCapacity();
    boolean remove(Object o);
    boolean contains(Object o);
    int drainTo(Collection&lt;? super E&gt; c);
    int drainTo(Collection&lt;? super E&gt; c, int maxElements);
}</code></pre>
<br/>


<h2 id="기능별-주요-메서드">기능별 주요 메서드</h2>
<p>대기 시 동작과 기능에 따라 아래와 같이 분류된다.
<img src="https://velog.velcdn.com/images/be_have98/post/41e44377-4229-490d-bf4a-4b485df2415d/image.png" alt=""></p>
<figcaption style="text-align:left; font-size:15px; color:#808080; margin-top: -40px;">
    출처: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/BlockingQueue.html
</figcaption>
<br/>

<h3 id="대기-시-동작">대기 시 동작</h3>
<p>즉시 락을 획득할 수 없는 경우 처리 방법에 따라 아래와 같이 네 가지로 나뉜다.</p>
<ul>
<li><code>Throws exception</code> : 대기 시 예외 발생</li>
<li><code>Special value</code> : 연산에 따라 <code>null</code> 또는 <code>false</code> 리턴</li>
<li><code>Blocks</code> : 연산 성공 시점까지 무기한 대기</li>
<li><code>Times Out</code> : 주어진 최대 시간 제한 동안 대기 후 <code>null</code> 또는 <code>false</code> 리턴<br/>

</li>
</ul>
<h2 id="주요-구현체">주요 구현체</h2>
<p><code>BlockingQueue</code> 인터페이스가 <code>Queue</code> 인터페이스를 상속 받으므로 구현체 모두 Collection Framework에 해당된다.</p>
<h3 id="arrayblockingqueue"><code>ArrayBlockingQueue</code></h3>
<pre><code class="language-java">public class ArrayBlockingQueue&lt;E&gt; extends AbstractQueue&lt;E&gt;
        implements BlockingQueue&lt;E&gt;, java.io.Serializable {

    final Object[] items;
    int takeIndex;
    int putIndex;
    int count;

    final ReentrantLock lock;
    private final Condition notEmpty;
    private final Condition notFull;
}</code></pre>
<ul>
<li>요소를 FIFO(선입선출) 방식으로 정렬</li>
<li>새 요소는 대기열의 끝에 삽입</li>
<li>고정된 크기의 배열에 요소를 보관하는 전형적인 <code>Bounded Buffer</code><br/>

</li>
</ul>
<h3 id="linkedblockingqueue"><code>LinkedBlockingQueue</code></h3>
<pre><code class="language-java">public class LinkedBlockingQueue&lt;E&gt; extends AbstractQueue&lt;E&gt;
        implements BlockingQueue&lt;E&gt;, java.io.Serializable {

     static class Node&lt;E&gt; {
        E item;

        Node&lt;E&gt; next;

        Node(E x) { item = x; }
    }

    private final int capacity;
    transient Node&lt;E&gt; head;
    private transient Node&lt;E&gt; last;

    private final ReentrantLock takeLock = new ReentrantLock();
    private final Condition notEmpty = takeLock.newCondition();

    private final ReentrantLock putLock = new ReentrantLock();
    private final Condition notFull = putLock.newCondition();
}</code></pre>
<ul>
<li>연결된 노드 기반으로 바인딩된 <code>Bounded Buffer</code></li>
<li>요소를 FIFO(선입선출) 방식으로 정렬</li>
<li>새 요소는 큐의 끝에 삽입</li>
<li>생성 시 사이즈를 지정하지 않으면 사이즈는 <code>Integer.MAX_VALUE</code><br/>
<br/>



</li>
</ul>
<h1 id="참고-자료">참고 자료</h1>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/locks/Condition.html">https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/locks/Condition.html</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/BlockingQueue.html">https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/BlockingQueue.html</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/ArrayBlockingQueue.html">https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/ArrayBlockingQueue.html</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/LinkedBlockingQueue.html">https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/LinkedBlockingQueue.html</a></li>
<li><a href="https://www.baeldung.com/java-blocking-queue">https://www.baeldung.com/java-blocking-queue</a></li>
<li><a href="https://www.baeldung.com/java-queue-linkedblocking-concurrentlinked">https://www.baeldung.com/java-queue-linkedblocking-concurrentlinked</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 생산자-소비자 문제와 Object 클래스]]></title>
            <link>https://velog.io/@be_have98/Java-%EC%83%9D%EC%82%B0%EC%9E%90-%EC%86%8C%EB%B9%84%EC%9E%90-%EB%AC%B8%EC%A0%9C%EC%99%80-Object-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@be_have98/Java-%EC%83%9D%EC%82%B0%EC%9E%90-%EC%86%8C%EB%B9%84%EC%9E%90-%EB%AC%B8%EC%A0%9C%EC%99%80-Object-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Wed, 13 Nov 2024 22:26:24 GMT</pubDate>
            <description><![CDATA[<h1 id="생산자-소비자-문제란">생산자-소비자 문제란?</h1>
<h2 id="개요">개요</h2>
<ul>
<li>여러 개의 프로세스를 어떻게 동기화할 것인지에 대한 문제로, 여러 스레드가 동시에 데이터를 생산 및 소비하는 과정에서 발생하는 문제를 말한다.</li>
<li>버퍼의 크기가 한정되어 있어 발생하는 문제이기 때문에 한정 버퍼 문제라고도 불린다.<br/>

</li>
</ul>
<h2 id="문제-상황">문제 상황</h2>
<p>한정된 버퍼가 존재하는 상황에서 아래와 같은 상황이 발생할 수 있다.</p>
<ol>
<li>버퍼가 가득 찬 경우, 생산자는 버퍼에 여유가 생길 때까지 대기</li>
<li>버퍼가 비어있는 경우, 소비자는 버퍼에 데이터가 존재할 때까지 대기<br/>
<br/>

</li>
</ol>
<h1 id="예상-처리-방안">예상 처리 방안</h1>
<ol>
<li>버퍼가 가득 찬 경우 생산자는 데이터를 생성하지 않거나 버퍼가 빈 경우 소비자는 데이터를 획득하지 않음 (❌)</li>
<li>버퍼가 가득 찬 경우 생산자는 버퍼에 여유가 생길 때까지 대기, 버퍼가 빈 경우 소비자는 버퍼에 데이터가 생성될 때까지 대기 (⭕)</li>
</ol>
<p>2번 방식으로 처리하기 위해서는 생산자 및 소비자 Thread가 락을 획득하고 무한대기 하지 않도록 아래와 같은 작업이 필요하다.</p>
<ul>
<li>현재 작업이 불가한 경우 락 반납 후 대기</li>
<li>작업 후 락을 반납한 뒤 대기중인 Thread를 깨움</li>
</ul>
<p>이때, Object 클래스의 <code>wait()</code>, <code>notify()</code>, <code>notifyAll()</code> 사용이 가능하다.
<br/>
<br/></p>
<h1 id="object-클래스의-thread-동기화-지원">Object 클래스의 Thread 동기화 지원</h1>
<h2 id="유관-메서드">유관 메서드</h2>
<h3 id="wait"><code>wait()</code></h3>
<pre><code class="language-java">public final void wait() throws InterruptedException</code></pre>
<ul>
<li>인터럽트 되거나 깨어날 때까지(Notify 알림을 받을 때까지) 대기</li>
<li><code>wait(0L, 0)</code> 이 호출된 것처럼 동작</li>
<li>호출 시 Thread의 상태는 <code>WAITING</code>으로 변경<br/>

</li>
</ul>
<h3 id="notify"><code>notify()</code></h3>
<pre><code class="language-java">public final void notify()</code></pre>
<ul>
<li><code>wait()</code> 메서드를 통해 해당 객체의 모니터에서 대기중인 Thread 중 하나를 깨움<ul>
<li>선택은 임의적이며 구현의 재량에 따라 달라짐</li>
</ul>
</li>
<li>현재 락을 획득한 Thread에서 호출 가능, 아닌 Thread가 호출 시 <code>IllegalMonitorStateException</code> 발생</li>
<li>해당 메서드 호출을 통해 깨어난 스레드는 락 획득 전까지 진행 불가<br/>

</li>
</ul>
<h3 id="notifyall"><code>notifyAll()</code></h3>
<pre><code class="language-java">public final void notifyAll()</code></pre>
<ul>
<li><code>wait()</code> 메서드를 통해 해당 객체의 모니터에서 대기중인 전체 Thread를 깨움</li>
<li>기타 다른 내용은 <code>notify()</code>와 유사<br/>
<br/>

</li>
</ul>
<h2 id="notify-의-한계"><code>notify()</code> 의 한계</h2>
<h3 id="비효율">비효율</h3>
<ul>
<li>특정 Thread를 깨울 수 없어 동일 타입의 Thread를 깨울 때 비효율 발생 가능<ul>
<li>ex) 버퍼가 비어있을 때 소비자 Thread가 다른 소비자 Thread를 깨우는 경우<br/>

</li>
</ul>
</li>
</ul>
<h3 id="기아-상태">기아 상태</h3>
<ul>
<li>특정 Thread를 깨울 수 없어 특정 스레드를 제외하고 깨울 수 있는 상황 발생 가능<ul>
<li>ex) 생산자 및 소비자 Thread가 <code>WAITING</code> 상태일 때 소비자 Thread들만 깨어날 수 있음<br/>
<br/>

</li>
</ul>
</li>
</ul>
<h1 id="javascript의-동시성">Javascript의 동시성</h1>
<p>위 내용을 작성하던 중 자바스크립트의 동작에 관해 궁금해졌다.</p>
<p>Javascript는 Single Thread로 동작하는 언어라  한 번에 하나의 작업만을 수행할 수 있다.</p>
<p>동시성을 보장하는 비동기, 논블로킹 작업들은 Javascript 엔진을 구동하는 <strong>런타임 환경</strong>에서 이루어진다.</p>
<p>이때, 런타임 환경이란 브라우저 혹은 Node.js 등을 의미한다.</p>
<p>하지만 정확히는 비동기 프로그래밍 패턴과 이벤트 루프를 통해 동시성을 지원하는 것처럼 보이게 할 수 있게 하는 것인데, 주요 방식은 아래와 같다.</p>
<h2 id="이벤트-루프">이벤트 루프</h2>
<ul>
<li>이벤트 루프를 통해 호출되는 콜백 함수 관리</li>
<li>비동기 작업 발생 시 큐에 작업들이 추가되고, 이벤트 루프는 순차적으로 콜백 함수 실행<br/>

</li>
</ul>
<h2 id="비동기-처리">비동기 처리</h2>
<pre><code class="language-java">async function fetchData() {
    try {
        let response = await fetch(&#39;https://api.example.com/data&#39;);
        let data = await response.json();
        console.log(data);
    } catch (error) {
        console.error(error);
    }
}
fetchData();</code></pre>
<ul>
<li><code>Promise</code> : 비동기 작업의 완료 또는 실패를 나타내는 객체</li>
<li><code>async</code> : <code>function</code> 앞에 사용하여 비동기 함수 정의</li>
<li><code>await</code> : <code>[rv] = await expression;</code>  형태로 사용되며, 프로미스를 기다리기 위해 사용<ul>
<li><code>async function</code> 내부에서만 사용 가능</li>
</ul>
</li>
</ul>
<br/>
<br/>
자세한 내용은 추후 다른 글을 통해 더 깊이 공부해보고 싶다.
<br/>
<br/>

<h1 id="참고-자료">참고 자료</h1>
<ul>
<li><a href="https://ko.wikipedia.org/wiki/%EC%83%9D%EC%82%B0%EC%9E%90-%EC%86%8C%EB%B9%84%EC%9E%90_%EB%AC%B8%EC%A0%9C">https://ko.wikipedia.org/wiki/생산자-소비자_문제</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Object.html">https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Object.html</a></li>
<li><a href="https://meetup.nhncloud.com/posts/89">https://meetup.nhncloud.com/posts/89</a></li>
<li><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/await">https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/await</a></li>
<li><a href="https://inpa.tistory.com/entry/%F0%9F%94%84-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84-%EA%B5%AC%EC%A1%B0-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC#%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8_%EC%9D%B4%EB%B2%A4%ED%8A%B8_%EB%A3%A8%ED%94%84_%EB%8F%99%EC%9E%91_%EA%B3%BC%EC%A0%95">https://inpa.tistory.com/entry/🔄-자바스크립트-이벤트-루프-구조-동작-원리#자바스크립트_이벤트_루프_동작_과정</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Lock 인터페이스와 ReentrantLock]]></title>
            <link>https://velog.io/@be_have98/Java-ReenterentLock</link>
            <guid>https://velog.io/@be_have98/Java-ReenterentLock</guid>
            <pubDate>Tue, 05 Nov 2024 23:00:25 GMT</pubDate>
            <description><![CDATA[<h1 id="🔎-등장-배경"><strong>🔎</strong> 등장 배경</h1>
<p><code>synchronized</code> 사용 시 발생하는 아래 문제점을 극복하기 위해 자바 1.5부터 <code>Lock</code> 인터페이스와 기본 구현체인 <code>ReentrantLock</code>을 제공하게 되었다.
<br/></p>
<h2 id="synchronized-한계"><code>synchronized</code> 한계</h2>
<h3 id="1-무한-대기">1) 무한 대기</h3>
<ul>
<li>락 획득 시까지 <code>BLOCKED</code> 상태 유지</li>
<li>이때 특정 시간동안 이후 발생하는 타임 아웃이나 인터럽트를 통한 대기 중단이 불가</li>
</ul>
<h3 id="2-공정성">2) 공정성</h3>
<ul>
<li><code>BLOCKED</code> 상태의 스레드 중 어느 스레드가 락을 획득할지 알 수 없음</li>
</ul>
<h3 id="3-유연성-저하">3) 유연성 저하</h3>
<ul>
<li>모든 락의 획득과 해제가 블록 구조 방식으로 이루어지므로 여러 개의 락 획득 시 획득 순서와 반대로 해제 필요</li>
<li>또한, 락의 해제는 락 획득 시 범위와 동일한 범위에서 해제 필요<br/>
<br/>

</li>
</ul>
<h1 id="🔒-lock-인터페이스">🔒 Lock 인터페이스</h1>
<p>Lock 인터페이스 구현 시 Lock의 획득과 해제에 대하여 <code>synchronized</code> 방식에 비해 유연성이 증가하게 된다.</p>
<p>락의 해제 시 획득 순서와 동일하지 않아도 되는 등 보다 유연성을 가지지만 구현 시 책임을 가지게 된다.</p>
<pre><code class="language-java">package java.util.concurrent.locks;

import java.util.concurrent.TimeUnit;

/**
 * (중략)
 * @see ReentrantLock
 * @see Condition
 * @see ReadWriteLock
 * @jls 17.4 Memory Model
 *
 * @since 1.5
 */
public interface Lock {

    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}
</code></pre>
<br/>

<h2 id="메소드">메소드</h2>
<h3 id="lock"><code>lock()</code></h3>
<pre><code class="language-java">void lock();</code></pre>
<ul>
<li>락 획득</li>
<li>다른 스레드가 락을 획득한 경우 스레드는 대기</li>
<li>인터럽트에 응답하지 않음<br/>


</li>
</ul>
<h3 id="lockinterruptibly"><code>lockInterruptibly()</code></h3>
<pre><code class="language-java">void lockInterruptibly() throws InterruptedException;</code></pre>
<ul>
<li><code>lock()</code>과 달리 락 획득 대기 중 인터럽트 발생 시 <code>InterruptedException</code> 발생 후 락 획득 포기<br/>


</li>
</ul>
<h3 id="trylock"><code>tryLock()</code></h3>
<pre><code class="language-java">boolean tryLock();</code></pre>
<ul>
<li>락 획득 시도 후 획득 여부 반환</li>
<li>획득 성공 여부를 즉시 반환하므로 인터럽트에 응답할 필요 없음<br/>

</li>
</ul>
<h3 id="trylocklong-time-timeunit-unit"><code>tryLock(long time, TimeUnit unit)</code></h3>
<pre><code class="language-java">boolean tryLock(long time, TimeUnit unit) throws InterruptedException;</code></pre>
<ul>
<li>매개변수의 시간 동안 락 획득 시도</li>
<li>대기 중 인터럽트 발생 시 <code>InterruptedException</code> 발생 후 락 획득 포기<br/>

</li>
</ul>
<h3 id="unlock"><code>unlock()</code></h3>
<pre><code class="language-java">void unlock();</code></pre>
<ul>
<li>락 해제</li>
<li>락을 획득한 스레드가 호출해야 함<br/>

</li>
</ul>
<h3 id="newcondition"><code>newCondition()</code></h3>
<pre><code class="language-java">Condition newCondition();</code></pre>
<ul>
<li>락과 결합하여 사용 가능한 <code>Condition</code> 객체 생성 후 반환</li>
<li><code>Condition</code> 객체는 스레드가 특정 조건을 기다리거나 신호를 받을 수 있도록 함<br/>

</li>
</ul>
<h2 id="사용-방법">사용 방법</h2>
<p>보통 아래와 같은 관용구를 사용하게 된다.</p>
<pre><code class="language-java"> Lock l = ...;
 l.lock();
 try {
   // 임계 구역: 락에 의해 자원 접근 시 보호됨
 } finally {
   l.unlock();
 }</code></pre>
<ul>
<li>락 획득/해제가 서로 다른 범위에서 발생하는 경우 락이 유지되는 동안 실행되는 모든 코드가 <code>try~catch</code> , <code>try~finally</code>로 보호되어 필요 시 잠금이 해제될 수 있도록 해야 한다.<br/>

</li>
</ul>
<h2 id="유의-사항">유의 사항</h2>
<p>세 가지 형태의 락 획득 방식(인터럽트 가능/불가능, 타이밍 적용)은 성능/순서 보장 등에서 차이가 존재할 수 있다.</p>
<p>구현체는 세 가지 형태의 락 획득 방식에 대해 동일하게 보장할 필요가 없으며, 진행 중인 락 획득의 인터럽트를 지원할 의무도 없다.</p>
<p>락 획득 시 인터럽트를 지원하는 경우, 인터럽트는 락 획득 전체 과정에서 지원되거나, 메서드 진입 시에만 지원될 수 있다.
<br/>
<br/></p>
<h1 id="🔐-lock-구현체">🔐 Lock 구현체</h1>
<p>인터페이스를 직접 구현하여 사용할 수도 있지만 자바에서는 다양한 구현체를 제공한다.</p>
<p>대표적인 구현체는 아래와 같으며, 구현체마다 세부 구현사항이 다를 수 있어 사용 시 확인 후 사용하는 것이 좋다.</p>
<h2 id="1-reentrantlock">1) <code>ReentrantLock</code></h2>
<p><code>synchronized</code> 메서드 및 구문을 통해 접근하는 모니터 락과 동일한 동작을 하며 추가 기능을 제공하는 재진입 가능한 상호 배제 락이다.</p>
<p>여기서 말하는 재진입은 동일한 스레드가 동일한 락을 여러 번 걸 수 있다는 의미이다.</p>
<p>마지막으로 락을 획득했지만 아직 해제하지 않은 스레드가 락을 소유하게 된다.</p>
<p>동일한 스레드 내 최대 2,147,483,647번의 재귀적 락을 지원하며, 한도 초과 시 락 메서드에서 <code>Error</code> 예외를 발생시킨다.</p>
<h3 id="공정성과-공정비공정-모드">공정성과 공정/비공정 모드</h3>
<pre><code class="language-java">public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync;

    ... 중략...

      public ReentrantLock() {
        sync = new NonfairSync(); // 비공정 모드
    }

    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync(); // 매개변수가 true일 경우 공정 모드
    }

    ... 중략...
}</code></pre>
<ul>
<li>생성자에 공정성 여부를 <code>true</code>로 넘길 경우, 경쟁 상황에서 가장 오랫동안 대기한 스레드에 락을 부여하는 것을 우선시하여 기아 현상이 발생하지 않도록 보장한다.</li>
<li>비공정 모드인 경우 특정 접근 순서를 보장하지 않는 대신 성능은 공정 모드에 비해 좋다.</li>
<li>하지만 락의 공정성이 스레드 스케줄링의 공정성을 보장하지는 않는다.</li>
<li><code>tryLock()</code> 메서드는 공정성을 따르지 않으며, 다른 스레드가 대기중이어도 락을 사용할 수 있으면 <code>true</code> 로 반환됨</li>
</ul>
<h3 id="메서드">메서드</h3>
<p><strong><code>isHeldByCurrentThread()</code></strong></p>
<pre><code class="language-java">public boolean isHeldByCurrentThread() {
    return sync.isHeldExclusively();
}</code></pre>
<ul>
<li>현재 스레드가 해당 락을 소유하고 있는지 확인</li>
<li>소유하고 있을 경우 <code>true</code>, 아닐 경우 <code>false</code> 반환</li>
</ul>
<p><strong><code>getHoldCount()</code></strong></p>
<pre><code class="language-java">public int getHoldCount() {
    return sync.getHoldCount();
}</code></pre>
<ul>
<li>현재 스레드가 해당 락을 몇 번 획득하고 있는지 반환, 이는 재진입 횟수를 의미</li>
</ul>
<h3 id="사용-예시">사용 예시</h3>
<pre><code class="language-java"> class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m() {
     lock.lock();
     try {
       // 임계 영역
     } finally {
       lock.unlock();
     }
   }
 }</code></pre>
<br/>

<h2 id="2-reentrantreadwritelock">2) <strong><code>ReentrantReadWriteLock</code></strong></h2>
<p><code>ReentrantLock</code> 과 유사하지만 읽기/쓰기 전용 락을 각각 가지고 있다는 점에서 차이가 있다.</p>
<p>또한, 특정 종류의 컬렉션을 사용할 때 동시성 개선에 사용될 수 있다.</p>
<p>단, 컬렉션이 크고, 쓰기보다 읽기 작업이 많고 동기화 오버헤드보다 더 큰 오버헤드를 가지는 작업인 경우에만 유용하다.</p>
<h3 id="공정성과-공정비공정-모드-1">공정성과 공정/비공정 모드</h3>
<pre><code class="language-java"> public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
    private final ReentrantReadWriteLock.ReadLock readerLock
    private final ReentrantReadWriteLock.WriteLock writerLock;
    final Sync sync;

    public ReentrantReadWriteLock() {
        this(false); // 비공정 모드
    }

    public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync(); // true일 경우 공정 모드
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }

    public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
    public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }

    ... 중략 ...
}</code></pre>
<ul>
<li><code>ReentrantLock</code> 과 동일하게 공정/비공정 모드 선택이 가능하다.</li>
</ul>
<h3 id="락의-강등">락의 강등</h3>
<pre><code class="language-java"> class CachedData {
   Object data;
   boolean cacheValid;
   final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

   void processCachedData() {
     rwl.readLock().lock();
     if (!cacheValid) {
       rwl.readLock().unlock();
       rwl.writeLock().lock();
       try {
         if (!cacheValid) {
           data = ...;
           cacheValid = true;
         }
         rwl.readLock().lock();
       } finally {
         rwl.writeLock().unlock(); // 읽기 락 획득 후 쓰기락 해제를 통한 락 강등
       }
     }

     try {
       use(data);
     } finally {
       rwl.readLock().unlock();
     }
   }
 }</code></pre>
<ul>
<li>쓰기 스레드는 읽기 락을 획득할 수 있지만, 반대의 경우는 불가능하다.
(ex. 쓰기 락을 보유한 스레드가 읽기 락 획득 가능)</li>
<li>이러한 특성으로 인해 쓰기 락을 획득한 상태에서 읽기 락을 획득한 경우 쓰기 락을 해제하여 락의 등급을 강등시킬 수 있으나, 읽기 락을 쓰기 락으로 업그레이드는 불가능하다.<br/>

</li>
</ul>
<h2 id="3-stampedlock"><strong>3) <code>StampedLock</code></strong></h2>
<p>읽기/쓰기 접근 제어를 위해 세 가지 모드를 제공하는 권한 기반의 락이며, 자바 1.8부터 제공되었다.</p>
<p><code>StampedLock</code>의 상태는 버전과 모드로 구성되고, 락 획득 메서드는 락 상태에 따른 접근을 제어할 수 있고 락의 상태를 나타내는 식별자인 스탬프를 반환한다.</p>
<p>락 해제 및 변환 메서드는 스탬프를 매개변수로 받아 락 상태와 일치하지 않으면 실패한다.</p>
<p>다른 구현체와 달리 <code>Lock</code> 또는 <code>ReadWriteLock</code> 인터페이스를 직접 구현하지 않았다.</p>
<pre><code class="language-java">public class StampedLock implements java.io.Serializable {
    ... 중략 ...

    private transient volatile Node head;
    private transient volatile Node tail;

    transient ReadLockView readLockView;
    transient WriteLockView writeLockView;
    transient ReadWriteLockView readWriteLockView;

    private transient volatile long state;
    private transient int readerOverflow;

    public StampedLock() {
        state = ORIGIN;
    }

    ... 중략 ...
}</code></pre>
<h3 id="모드">모드</h3>
<p>스탬프를 어떤 방식으로 획득하였는지에 따라 아래와 같이 나뉜다.</p>
<ol>
<li><strong>쓰기 모드</strong> : <code>writeLock()</code> 사용 시</li>
<li><strong>읽기 모드</strong> : <code>readLock()</code> 사용 시</li>
<li><strong>낙관적 읽기 모드</strong> : <code>tryOptimisticRead()</code> 사용 시</li>
</ol>
<h3 id="모드-업그레이드">모드 업그레이드</h3>
<p><code>tryConvertToWriteLock(long stamp)</code> 메서드를 통해 아래 상황에서 쓰기 모드로의 업그레이드를 지원한다.</p>
<ul>
<li>이미 쓰기 모드인 경우</li>
<li>읽기 모드이고 다른 읽기 스레드가 없는 경우</li>
<li>낙관적 읽기 모드이고 락이 사용한 경우</li>
</ul>
<h3 id="주의사항">주의사항</h3>
<ul>
<li>재진입이 불가하여 락이 걸린 상태에서 락을 획득하려고 하면 안 된다.</li>
<li>스케줄링 시 읽기 모드나 쓰기 모드와 같은 특정 모드를 우선시 하지 않음</li>
<li><code>try~</code> 메서드는 공정성을 보장하지 않을 수 있음<br/>
<br/>

</li>
</ul>
<h1 id="📚-참고-자료"><strong>📚</strong> 참고 자료</h1>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/locks/Lock.html">https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/locks/Lock.html</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/locks/ReentrantLock.html">https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/locks/ReentrantLock.html</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/locks/ReentrantReadWriteLock.html">https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/locks/ReentrantReadWriteLock.html</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/locks/StampedLock.html">https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/locks/StampedLock.html</a></li>
<li><a href="https://www.baeldung.com/java-concurrent-locks">https://www.baeldung.com/java-concurrent-locks</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 메모리 가시성과 volatile]]></title>
            <link>https://velog.io/@be_have98/Java-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B0%80%EC%8B%9C%EC%84%B1%EA%B3%BC-volatile</link>
            <guid>https://velog.io/@be_have98/Java-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B0%80%EC%8B%9C%EC%84%B1%EA%B3%BC-volatile</guid>
            <pubDate>Wed, 30 Oct 2024 00:12:55 GMT</pubDate>
            <description><![CDATA[<h1 id="🔒-상황">🔒 상황</h1>
<p>멀티 스레드를 사용하는 상황에서 <code>Runnable</code>을 구현하여 실행한 다수의 Thread가 Thread 내부 변수의 변경된 값을 읽지 못하는데,
이러한 문제를 <strong>메모리 가시성</strong> 문제라고 한다.</p>
<h2 id="예시">예시</h2>
<pre><code class="language-java">public class TaskRunner {

    private static class Task implements Runnable {

        boolean flag = true;

        @Override
        public void run() {
            while (!flag) {}

            System.out.println(&quot;Task 종료&quot;);
        }
    }

    public static void main(String[] args) {
        Task task = new Task();
        task.start();

        task.flag = false;
    }
}</code></pre>
<br/>
<br/>

<h1 id="🔎-원인">🔎 원인</h1>
<p>문제는 상대적으로 속도가 느린 메모리의 접근 횟수를 줄이기 위해 값을 캐싱하여 사용하는 데 있다.
캐시는 메모리 중 일부를 미리 가져오고 CPU는 메모리에 접근 전 먼저 캐시 내 원하는 데이터가 존재하는지 확인한다.
이때 L1 ➡ L2 ➡ L3 순으로 캐시 검색 후 캐시 미스 시 메모리를 조회한다.</p>
<p>위 코드 기준으로 <code>Task</code> Thread는 메모리에 반영된 변경 값(<code>false</code>)이 캐시에 반영 전까지 <code>flag</code> 변수를 <code>true</code>로 참조하여 예상대로 동작하지 않는 것이다.</p>
<h2 id="캐시-구조">캐시 구조</h2>
<p>L1 ➡ L2 ➡ L3 순으로 속도가 점차 느려지며, 용량은 커진다.
일부 캐시는 프로세서별로 구조가 상이할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/be_have98/post/7f2646b2-ecc3-4cdb-995d-0dc35967c54a/image.png" alt="캐시 구조"></p>
<h3 id="l1-캐시">L1 캐시</h3>
<ul>
<li>직접 레지스터에 연결된 캐시로, 명령어 캐시와 데이터 캐시가 존재</li>
<li>명령어와 데이터를 구분하여 조회하는 특수 캐시</li>
<li>캐시 중 가장 빠르고 용량이 작음</li>
</ul>
<h3 id="l2-캐시">L2 캐시</h3>
<ul>
<li>CPU 코어 또는 코어 근처에 위치하여 코어별로 사용되거나 코어들이 공유</li>
<li>L1 캐시가 커버하지 못하는 데이터와 명령어 저장</li>
</ul>
<h3 id="l3-캐시">L3 캐시</h3>
<ul>
<li>CPU 내부에 위치</li>
<li>캐시 중 가장 용량이 큼</li>
</ul>
<blockquote>
</blockquote>
<p><strong>🚌 버스란?</strong></p>
<ul>
<li>메인보드에서 각 장치를 연결하여 데이터가 지나다니는 통로</li>
</ul>
<blockquote>
</blockquote>
<p><strong>🚌 CPU 내부 버스란?</strong></p>
<ul>
<li>CPU 내부 장치를 연결하는 버스로, BSB(Back Side Bus) 또는 후면 버스라 불림</li>
</ul>
<blockquote>
</blockquote>
<p><strong>🚌 시스템 버스란?</strong></p>
<ul>
<li>메모리와 주변 방치를 연결하는 버스로, FSB(Front Side Bus) 또는 전면 버스라 불림</li>
<li>메인보드의 동작 속도를 의미</li>
</ul>
<br/>

<h2 id="캐시-히트와-캐시-미스">캐시 히트와 캐시 미스</h2>
<h3 id="캐시-히트">캐시 히트</h3>
<ul>
<li>캐시에서 원하는 데이터를 찾은 경우</li>
</ul>
<h3 id="캐시-미스">캐시 미스</h3>
<ul>
<li>캐시에서 원하는 데이터를 찾지 못하여 메모리 내 데이터를 조회하는 경우</li>
</ul>
<p>일반적인 컴퓨터의 캐시 적중률은 약 90%라고 한다.</p>
<br/>
<br/>


<h1 id="🔑-해결-방법">🔑 해결 방법</h1>
<p>Thread 내 공유 변수에 <strong><code>volatile</code> 키워드</strong>를 사용한다.
단, 메모리 내 데이터를 직접 읽고 쓰기 때문에 성능은 저하된다.</p>
<p>사용하지 않을 경우 캐시 메모리 갱신 전까지 메모리와 캐시 간 데이터가 일치하지 않는 문제가 발생할 수 있다.
주로 컨텍스트 스위칭 시 캐시 메모리도 함께 갱신되지만 이는 달라질 수 있다.</p>
<pre><code class="language-java">public class TaskRunner {

    private static class Task implements Runnable {

        volatile boolean flag = true; // volatile 키워드 사용!

        @Override
        public void run() {
            while (!flag) {}

            System.out.println(&quot;Task 종료&quot;);
        }
    }

    public static void main(String[] args) {
        Task task = new Task();
        task.start();

        task.flag = false;
    }
}</code></pre>
<br/>
<br/>


<h1 id="🔧-함께-알아두기">🔧 함께 알아두기</h1>
<h2 id="저장-장치-계층-구조">저장 장치 계층 구조</h2>
<blockquote>
<p>레지스터 ➡ 캐시 ➡ 메모리 ➡ 저장장치</p>
</blockquote>
<p>위 순으로 속도가 느려지며, 용량은 커진다.</p>
<br/>

<h2 id="캐시-내-변경사항-반영-방법">캐시 내 변경사항 반영 방법</h2>
<p>캐시 내 변경사항을 메모리에 반영하는 방법은 아래와 같다.</p>
<h3 id="1-즉시-쓰기-write-through">1) 즉시 쓰기 (Write Through)</h3>
<ul>
<li>캐시 내 데이터 변경 즉시 메모리에 반영</li>
<li>메모리와의 빈번한 데이터 전송으로 성능 저하</li>
</ul>
<h3 id="2-지연-쓰기-write-back">2) 지연 쓰기 (Write Back)</h3>
<ul>
<li>캐시 내 데이터 변경 시 변경사항을 모아 주기적으로 반영</li>
<li>성능상 이점이 존재하지만 메모리와 캐시 간 데이터 불일치 발생 가능</li>
</ul>
<br/>
<br/>




<h1 id="📚-참고-자료">📚 참고 자료</h1>
<ul>
<li>[도서] 쉽게 배우는 운영체제 </li>
<li><a href="https://it.donga.com/215/">https://it.donga.com/215/</a></li>
<li><a href="https://www.baeldung.com/java-volatile">https://www.baeldung.com/java-volatile</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Thread.sleep()과 InterruptedException]]></title>
            <link>https://velog.io/@be_have98/Thread.sleep%EA%B3%BC-InterruptedException</link>
            <guid>https://velog.io/@be_have98/Thread.sleep%EA%B3%BC-InterruptedException</guid>
            <pubDate>Wed, 23 Oct 2024 12:24:39 GMT</pubDate>
            <description><![CDATA[<h1 id="threadsleep"><code>Thread.sleep()</code></h1>
<p><code>Thread.sleep()</code> 은 Thread가 특정 시간동안 대기해야 할 때 사용되는 메서드</p>
<p>테스트 시 사용되는 등 종종 아래와 같이 사용될 때가 있음</p>
<h2 id="예시">예시</h2>
<pre><code class="language-java">public static void sleep(long millis) {
    try {
        Thread.sleep(millis);
    } catch (**InterruptedException ex**) {
        // 적절히 처리 필요!
    }
}</code></pre>
<br/>
<br/>

<h1 id="interruptedexcpetion"><code>InterruptedExcpetion</code></h1>
<p>위와 같이 <code>Thread.sleep()</code> 에서 지정한 시간이 지나면 발생하는 체크 예외로, 아래 조건에서 발생</p>
<h2 id="발생-원인">발생 원인</h2>
<ul>
<li>Thread가 대기중 또는 <code>Thread.sleep()</code>와 같이 수면중이거나 다른 방식으로 점유중일 때 발생하고, 활동 전 또는 활동 중에 스레드가 중단될 때 발생</li>
</ul>
<br/>

<h2 id="활용-방안">활용 방안</h2>
<p>현재 스레드의 인터럽트 여부 확인 후 아래와 같이 예외 발생시킬 수 있음</p>
<pre><code class="language-java">if (thread.interrupted()) throw new InterruptedException();</code></pre>
<br/>
<br/>

<h1 id="인터럽트란">인터럽트란?</h1>
<p>Thread에 하던 일을 중단하라는 신호를 보내기 위해 사용</p>
<h2 id="동작-방식">동작 방식</h2>
<ol>
<li>특정 Thread에 인터럽트를 발생시키기 위해 해당 Thread의 <code>interrupt()</code> 메서드 호출</li>
<li>해당 Thread의 인터럽트 플래그가 <code>true</code> 로 변경</li>
<li>특정 조건에 해당되지 않는 경우 현재 Thread에 인터럽트 상태 설정 (<code>interrupt()</code> 참고)<br/>

</li>
</ol>
<h2 id="인터럽트-발생-여부-확인-방안">인터럽트 발생 여부 확인 방안</h2>
<ol>
<li><code>InterruptedException</code> 예외가 발생 여부 확인 (❌)<ul>
<li>인터럽트 발생 시 항상 예외가 발생하는 것이 아니므로 올바른 방법이 아님</li>
</ul>
</li>
<li><code>interrupted()</code>, <code>isInterrupted()</code> 호출을 통해 Thread 내 인터럽트 플래그 확인 (⭕)<br/>

</li>
</ol>
<h2 id="유관-메서드">유관 메서드</h2>
<h3 id="interrupt"><code>interrupt()</code></h3>
<pre><code class="language-java">public void interrupt()</code></pre>
<ul>
<li>현재 Thread가 수정될 수 없는 상태인 경우 <code>SecurityException</code> 발생 가능</li>
<li>아래 조건에 해당되지 않는 경우 현재 Thread에 인터럽트 상태 설정<ul>
<li><code>wait()</code>, <code>join()</code>, <code>sleep()</code> 메서드에서 호출이 차단되면 인터럽트 플래그가 <code>false</code>로 변경되고 <code>InterruptedException</code> 발생</li>
<li>Thread가 <a href="https://docs.oracle.com/javase/8/docs/api/java/nio/channels/InterruptibleChannel.html"><code>InterruptibleChannel</code></a> 의 I/O 작업에서 블로킹된 상태인 경우 인터럽트 플래그가 <code>true</code>로 설정되고 <code>ClosedByInterruptException</code> 발생</li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/nio/channels/Selector.html"><code>Selector</code></a>에서 차단된 경우 Thread 인터럽트 플래그를 <code>true</code>로 설정 후 선택 작업을 <code>0</code>이 아닌 값으로 즉시 반환</li>
</ul>
</li>
<li>살아 있지 않은 Thread를 인터럽트하면 소용 없음</li>
</ul>
<blockquote>
</blockquote>
<p>💡 <strong>InterruptibleChannel이란?</strong></p>
<ul>
<li>java.nio.channels 패키지 내 클래스로, 비동기적으로 닫고 중단 가능한 네트워크 연결에 사용되는 채널</li>
<li><code>FileChannel</code>, <code>SocketChannel</code> 등이 해당 인터페이스를 구현</li>
</ul>
<blockquote>
</blockquote>
<p>💡 <strong>Selector란?</strong></p>
<ul>
<li>Non-Blocking I/O 연산을 관리하는 데 사용</li>
<li>단일 스레드로 여러 네트워크 채널의 I/O 이벤트를 효율적으로 처리 가능</li>
</ul>
<br/>


<h3 id="interrupted"><code>interrupted()</code></h3>
<pre><code class="language-java">public static boolean interrupted()</code></pre>
<ul>
<li>현재 Thread의 중단 여부 판단</li>
<li><strong>호출 시 Thread가 인터럽트 상태라면 <code>true</code> 반환 후 인터럽트 플래그는 <code>false</code>로 변경됨</strong></li>
<li>살아 있지 않은 Thread인 경우 <code>false</code> 반환<br/>

</li>
</ul>
<h3 id="isinterrupted"><strong><code>isInterrupted()</code></strong></h3>
<pre><code class="language-java">public boolean isInterrupted()</code></pre>
<ul>
<li>현재 Thread의 중단 여부 판단</li>
<li><code>interrupted()</code>와 달리 인터럽트 플래그 변경을 진행하지 않음</li>
<li>살아 있지 않은 Thread인 경우 <code>false</code> 반환</li>
</ul>
<br/>
<br/>

<h2 id="📚-참고-자료">📚 참고 자료</h2>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Thread.html#interrupted()">https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Thread.html#interrupted()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/InterruptedException.html">https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/InterruptedException.html</a></li>
<li><a href="https://docs.oracle.com/javase/tutorial/essential/concurrency/interrupt.html">https://docs.oracle.com/javase/tutorial/essential/concurrency/interrupt.html</a></li>
<li><a href="http://happinessoncode.com/2017/10/09/java-thread-interrupt/">http://happinessoncode.com/2017/10/09/java-thread-interrupt/</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Git] Git-Flow 사용 시 Feature의 Base 브랜치 변경하기]]></title>
            <link>https://velog.io/@be_have98/Git-Git-Flow-%EC%82%AC%EC%9A%A9-%EC%8B%9C-Feature%EC%9D%98-Base-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@be_have98/Git-Git-Flow-%EC%82%AC%EC%9A%A9-%EC%8B%9C-Feature%EC%9D%98-Base-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 08 Apr 2022 17:11:52 GMT</pubDate>
            <description><![CDATA[<h1 id="🔒-상황">🔒 상황</h1>
<p>일부 프로젝트에서 <code>Git-Flow</code> 를 통해 <code>Feature</code> 브랜치를 생성할 때 <code>develop</code> 브랜치가 아닌 기존에 존재하던 다른 이름의 브랜치를 Base로 생성하는 방법이 필요하게 되었다. </p>
<p>해당 프로젝트는 개발 초기부터 <code>Git-Flow</code> 를 통해 개발을 진행하지 않아 <code>develop</code> 이 아닌 다른 브랜치를 <code>develop</code> 브랜치처럼 사용하고 있었기 때문이다.</p>
<br/>

<h1 id="🔑-해결-방법">🔑 해결 방법</h1>
<h4 id="1-해당-프로젝트가-존재하는-폴더-하위의-git-폴더로-이동한다">1. 해당 프로젝트가 존재하는 폴더 하위의 <code>.git</code> 폴더로 이동한다.</h4>
<p><img src="https://velog.velcdn.com/cloudflare/be_have98/83b4a785-824e-49ba-aaab-7731f41a8dfb/image.png" alt="이미지 1"></p>
<h4 id="2-config-파일을-열어서-아래-내용을-수정한다">2. <code>config</code> 파일을 열어서 아래 내용을 수정한다.</h4>
<p><img src="https://velog.velcdn.com/cloudflare/be_have98/ceea2e79-eebd-4ff1-9ad5-90f2e7d8a36e/image.png" alt=""></p>
<pre><code>[gitflow &quot;branch&quot;]
    master = {git-flow의 master 역할을 할 브랜치}
    develop = {git-flow의 develop 역할을 할 브랜치}</code></pre><br/>

<h1 id="📕-느낀-점">📕 느낀 점</h1>
<ul>
<li>까먹기 전에 자주 자주 기록해두어야겠다. 😂</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[실용주의 프로그래머] 복습 및 미션(4) + 후기]]></title>
            <link>https://velog.io/@be_have98/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-%EB%B3%B5%EC%8A%B5-%EB%B0%8F-%EB%AF%B8%EC%85%984</link>
            <guid>https://velog.io/@be_have98/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-%EB%B3%B5%EC%8A%B5-%EB%B0%8F-%EB%AF%B8%EC%85%984</guid>
            <pubDate>Wed, 06 Apr 2022 15:40:21 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h2 id="3-주차">3 주차</h2>
</blockquote>
<h3 id="수-목--assignment-16">수, 목 | Assignment #16</h3>
<ul>
<li>📚 복습</li>
<li>✔️ 미션 Mission(4)</li>
</ul>
<br/>

<h2 id="mission4">Mission(4)</h2>
<hr/>

<h3 id="📌-인상깊은-10가지-tip">📌 인상깊은 10가지 TIP</h3>
<ol>
<li><p><strong>어설픈 변명 말고 대안을 제시하라.</strong>
→ 변명하는 대신 대안을 제시하라. 그 일은 할 수 없다고만 말하지 말고, 무엇을 할 수 있는지 설명하라.</p>
</li>
<li><p><strong>최종 결정이란 없다.</strong>
→ 돌에 새겨진 것처럼 바뀌지 않는 결정은 없다. 모든 결정이 바닷가의 모래 위에 쓰인 글씨라 생각하고 변화에 대비하라.</p>
</li>
<li><p><strong>여러분은 완벽한 소프트웨어를 만들 수 없다.</strong>
→ 소프트웨어는 완벽할 수 없다. 불가피한 오류로부터 여러분의 코드와 사용자를 보호하라.</p>
</li>
<li><p><strong>우연에 맡기는 프로그래밍은 하지 말라.</strong>
→ 믿을 만한 것만 믿어야 한다. 우발적인 복잡성에 주의하고, 우연한 행운을 목적을 가지고 세운 계획으로 착각하지 말라.</p>
</li>
<li><p><strong>일찍 리팩토링하고, 자주 리팩토링하라.</strong>
→ 정원의 잡초를 뽑고 식물 배치를 바꾸는 것과 같이 코드도 필요할 때마다 다시 작성하고, 다시 작업하고, 아키텍처를 다시 만들라. 근본적인 문제를 해결하라.</p>
</li>
<li><p><strong>테스트할 수 있도록 설계하라.</strong>
→ 코드를 쓰기 전 테스트에 대해 먼저 생각하라.</p>
</li>
<li><p><strong>요구 사항은 피드백을 반복하며 알게 된다.</strong>
→ 요구 사항을 이해하려면 탐험과 피드백을 거쳐야 한다. 결정한 요구 사항의 여파를 바탕으로 처음의 발상을 다듬는다.</p>
</li>
<li><p><strong>생각의 틀을 벗어나지 말고, 틀을 찾아라.</strong>
→ 불가능한 문제와 마주쳤다면 진짜 제약 조건을 찾아라. 스스로 물어보라. &#39;정말로 반드시 이렇게 해야 하나? 꼭 해야 하는 일이긴 한가?&#39;</p>
</li>
<li><p>모든 테스트가 끝날 때까지는 코딩이 끝난 게 아니다.
→ 당연하다.</p>
</li>
<li><p>사용자를 기쁘게 하라. 그저 코드만 내놓지 말라.
→ 사용자의 사업 가치를 창조하는 해법을 개발하라. 실행할 것이다.</p>
</li>
</ol>
<br/>

<h3 id="📒-챌린지-후기">📒 챌린지 후기</h3>
<hr/>

<ul>
<li><code>클린코드 챌린지</code> 완주 직후에 야근 등 다른 일과 병행하며 &#39;새로운 챌린지를 또 해낼 수 있을까?&#39;, &#39;완주 못할 바에는 시작도 하지 않는 게 낫지 않을까?&#39;란 생각이 챌린지 시작 전에 자꾸만 떠올랐었다. 하지만 막상 시작해보니 3주는 금방 흘러갔고 벌써 두 번째의 챌린지를 완주하게 되어 뿌듯하다. 그리고 정해진 챌린지 일정을 진행해야 하다 보니 약간의 강제성이 부여되어 하차하지 않고 평소에 읽고 싶었던 책을 다 읽을 수 있게 된 것 같다. </li>
<li>최근에는 특정 기술만을 다루는 기술 서적을 주로 읽었던 것 같은데 이러한 책을 읽다보니 &quot;개발자&quot;라는 직업에 대한 생각이 많아졌는데 앞으로도 종종 이러한 류의 책을 읽어야겠다고 생각했다.</li>
</ul>
<br/>

]]></description>
        </item>
        <item>
            <title><![CDATA[[실용주의 프로그래머] 9장. 실용주의 프로젝트]]></title>
            <link>https://velog.io/@be_have98/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-9%EC%9E%A5-%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8</link>
            <guid>https://velog.io/@be_have98/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-9%EC%9E%A5-%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8</guid>
            <pubDate>Tue, 05 Apr 2022 14:50:42 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h2 id="3-주차">3 주차</h2>
</blockquote>
<h3 id="화--assignment-15">화 | Assignment #15</h3>
<ul>
<li>📚 9장. 실용주의 프로젝트</li>
<li>✔️ TIL</li>
</ul>
<br/>  

<h2 id="9장-실용주의-프로젝트">9장. 실용주의 프로젝트</h2>
<hr/>

<h3 id="📘-책에서-기억하고-싶은-내용">📘 책에서 기억하고 싶은 내용</h3>
<ul>
<li><p><strong>개요</strong></p>
<ul>
<li>실용주의 기법들을 개인이 아닌 팀에 확장하여 적용시키면 이점이 몇 배로 많아진다.</li>
</ul>
</li>
<li><p><strong>실용주의 팀</strong> (p.378)</p>
<ul>
<li>실용주의 팀은 작다.<ul>
<li>구성원이 대략 10~12명 이하</li>
<li>구성원이 추가되거나 빠지는 일은 거의 없음</li>
<li>모두가 서로 잘 알고, 신뢰하며, 의존해야 함</li>
</ul>
</li>
<li>팀 전체가 깨진 창문을 용납하지 않아야 한다.</li>
<li>품질은 팀원 모두가 제각기 기여할 때만 보장된다. (품질은 애초에 제품에 포함된 것)</li>
<li>모든 사람이 적극적으로 환경 변화를 감시하도록 권장해야 한다.</li>
<li>우리 팀이 진정 개선하고 혁신하고 싶다면 계획을 세워야 한다.
→ &quot;시간이 나면 그때&quot; 하겠다는 것은 &quot;영원히 하지 않겠다&quot;는 것</li>
<li>팀의 지식 포트폴리오 계획<ul>
<li>구형 시스템 유지 보수 : 미루지 말고 처리하라</li>
<li>프로세스 회고와 개선 : 무엇이 잘되고 잘되지 않았는지 지속적인 확인을 통한 계획 수립 및 변경 필요</li>
<li>새로운 기술 탐험 : 유행한다는 이유로 도입하지 말고 프로토타이핑을 통하여 신중한 조사 필요</li>
<li>학습 및 기술 갈고 닦기 : 많은 기술을 팀원들에게 전도할 계획을 세워라(스몰토크, 스터디 등)</li>
</ul>
</li>
<li><code>DRY 원칙</code> 을 지키기 위해서는 팀원 서로에게 관심을 유지하라. (동료의 업무 등)</li>
<li>예광탄을 내가 맡은 업무가 아닌 전체 업무 영역에서 활용해보자. (UX, F/E, B/E, Server, DB, QA 등)</li>
<li>팀은 개인들로 이루어진다. 각 팀원</li>
</ul>
</li>
<li><p><strong>코코넛만으로는 부족하다</strong> (p.387)</p>
<ul>
<li>유행하는 것(정책, 프로세스 등)이 아니라 성공 사례와 동일한 제약 조건을 가지고 있는지 확인하여 실제로 잘 맞는 것을 새용해야 한다.</li>
<li>&quot;잘 맞는 것&quot; 을 어떻게 알까?
→ 실용주의 기법을 적용하여 실험하며 좋은 부분만 유지</li>
<li>소프트웨어 개발 방법론의 목표는 사람들이 함께 일하는 것을 돕는 것이므로, 어떤 특정 방법론에서 가장 좋은 부분만 가져다가 적절히 조정하여 사용해야 한다.</li>
<li>특정 방법론에 과도하게 투자하면 다른 대안을 보지 못하게 될 수도 있다.</li>
</ul>
</li>
<li><p><strong>실용주의 시작 도구</strong> (p.392)</p>
<ul>
<li><em>&quot;테스팅은 버그의 존재만 보여줄 수 있지 버그의 부재까지는 보여줄 수 없다.&quot; - 데이크스트</em></li>
<li><code>버전 관리</code> , <code>가차 없는 테스트</code> , <code>전체 자동화</code> 는 프로젝트에 필수적인 견고한 기반이다.</li>
<li>빌드/릴리즈/테스트/문서 등 프로젝트에서 거듭 발생하는 작업은 모두 자동화가 필요하다.</li>
<li>많은 개발자들이 무의식적으로 코드가 어디에서 깨지는지 파악하고서는 약한 지점을 피해 다니면서 살살 테스트하려 한다.</li>
<li>일찍 테스트하고, 자주 테스트하라. 테스트를 자동화하라.</li>
<li>모든 테스트가 끝날 때까지는 코딩이 끝난 게 아니다.</li>
<li>빌드 과정에는 테스트 주요 유형이 포함되어야 한다. (단위 테스트, 통합 테스트, 성능 테스트 등)</li>
<li>어떤 버그를 감지해 내는 테스트를 작성한 후에, 그 버그가 의도적으로 생기도록 한 다음 테스트가 경보를 울리는지 확인하라.</li>
<li><code>코드 커버리지</code> 보다 중요한 것은 프로그램이 갖는 <code>상태</code> 이다.</li>
</ul>
</li>
<li><p><strong>사용자를 기쁘게 하라</strong> (p.402)</p>
<ul>
<li>모든 팀 구성원이 사용자가 기대하는 바를 완전히 이해해야 한다.</li>
<li>결정을 내릴 때면 어떤 선택이 사용자의 기대에 더 가깝게 가는 길인지 생각해보아야 한다.</li>
<li>이런 기대를 염두에 두고 사용자의 요구 사항을 비판적으로 분석하라.</li>
<li>프로젝트를 진행하면서도 계속 사용자의 기대에 대하여 생각하라.</li>
<li>실용주의 프로그래머의 본질은 &quot;문제 해결사&quot;고, 문제를 해결한다.</li>
</ul>
</li>
<li><p><strong>오만과 편견</strong> (p.404)</p>
<ul>
<li>자신의 작품에 서명하는 것을 자랑스러워할 수 있어야 한다.</li>
<li>자신의 코드만 좋게 보고 동료들의 코드는 깎아내리는 편견을 갖지 마라.</li>
<li>경계심 때문에 나의 코드를 참견하는 사람으로부터 방어하려고 해서는 안 된다.</li>
</ul>
</li>
</ul>
<br/>

<h3 id="🤔-소감-및-생각">🤔 소감 및 생각</h3>
<ul>
<li>드디어 마지막 챕터를 다 읽었다. 책을 다 읽고 나니 앞으로 내가 어떻게 해야 할지 한참을 고민하게 되었다. 책에서 말하는 실용주의 기법은 완전히 새로운 것이 아닌, 대부분 모두가 알고 있지만 지키기 어려운 원칙이었던 것 같다. </li>
<li>읽으면서 &#39;아 이런 부분은 실무에서 이러한 사유때문에 적용하기 어려울텐데&#39; 이런 생각을 자주 하였는데, 이런 방어적인 생각때문에 나와 팀이 더 발전할 기회를 놓쳤을 수도 있다는 생각이 들었다. 사소한 부분이라도 반복되는 업무는 자동화를 하며 업무 시간을 코드 작성에 많이 할애할 수 있도록 노력해야겠다.</li>
</ul>
<br/> 

<h3 id="🔍-새롭게-또는-다시-알게-된-내용">🔍 새롭게 또는 다시 알게 된 내용</h3>
<ul>
<li><del><em>NONE</em></del></li>
</ul>
<br/>
]]></description>
        </item>
        <item>
            <title><![CDATA[[실용주의 프로그래머] 복습 및 미션(3)]]></title>
            <link>https://velog.io/@be_have98/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-%EB%B3%B5%EC%8A%B5-%EB%B0%8F-%EB%AF%B8%EC%85%983</link>
            <guid>https://velog.io/@be_have98/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-%EB%B3%B5%EC%8A%B5-%EB%B0%8F-%EB%AF%B8%EC%85%983</guid>
            <pubDate>Mon, 04 Apr 2022 16:27:31 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h2 id="3-주차">3 주차</h2>
</blockquote>
<h3 id="월--assignment-14">월 | Assignment #14</h3>
<ul>
<li>📚 복습</li>
<li>✔️ 미션 Mission(3)</li>
</ul>
<br/>

<h2 id="mission3">Mission(3)</h2>
<hr/>

<h3 id="📌-연습문제-33">📌 연습문제 33</h3>
<p>다음 문장들이 진정한 요구 사항인가? 가능하다면 진정한 요구사항이 아닌 것을 좀 더 유용하게 고쳐 써 보라.</p>
<h4 id="1-응답시간은-500ms-이하여야-한다">1. 응답시간은 500ms 이하여야 한다.</h4>
<ul>
<li><p>📖 책의 해답</p>
<ul>
<li>이 문장은 진짜 요구 사항처럼 보인다. 환경 때문에 애플리케이션에 제약을 추가해야 할 수 있다.</li>
</ul>
</li>
<li><p>💡 나의 해답</p>
<ul>
<li>아래 내용에 대하여 추가적으로 확인이 필요하다.<ul>
<li>이 속도가 최소 요구 조건인지?</li>
<li>응답시간 측정 시 시작 시간의 기준이 API 호출 직후인지, 클라이언트에서 사용자가 액션을 한 시점인지</li>
<li>타 시스템 API 사용 등 여러 요인으로 인하여 응답시간이 최소 500ms 이상인 경우에는 기능을 제외할 것인지</li>
<li>네트워크 속도(성능) 수준은 어떤지</li>
</ul>
</li>
</ul>
</li>
</ul>
<br/>

<h4 id="2-모달-창의-바탕색은-회색이다">2. 모달 창의 바탕색은 회색이다.</h4>
<ul>
<li><p>📖 책의 해답</p>
<ul>
<li>이 문장 자체만으로는 진짜 요구사항이 아니다. 하지만 진짜로 무엇이 필요한지 알아내려면 마법의 질문을 던져야 한다. ”왜?”(중략)</li>
</ul>
</li>
<li><p>💡 나의 해답</p>
<ul>
<li>UI 요구사항의 아래 항목에 대하여 추가적으로 확인이 필요하다.<ul>
<li>글자색</li>
<li>버튼 등 컴포넌트의 표준 색상</li>
<li>표준 레이아웃</li>
</ul>
</li>
</ul>
</li>
</ul>
<br/>

<h4 id="3-애플리케이션은-프론트엔드-프로세스-몇-개와-백엔드-서버로-구성된다">3. 애플리케이션은 프론트엔드 프로세스 몇 개와 백엔드 서버로 구성된다.</h4>
<ul>
<li><p>📖 책의 해답</p>
<ul>
<li>이 문장은 요구사항이 아니다. 이것은 아키텍처다. 이런 종류의 것과 마주쳤다면 사용자가 무슨 생각을 하는지 알아내기 위해 깊이 파고들어야 한다.</li>
</ul>
</li>
<li><p>💡 나의 해답</p>
<ul>
<li>전체적인 요구사항 파악 후 인프라 및 아키텍처 설계가 가능함을 설명하기 전, 해당 항목의 진짜 의도를 파악하기 위해 질문한다.</li>
</ul>
</li>
</ul>
<br/>

<h4 id="4-사용자가-숫자가-아닌-글자를-숫자-필드에-입력하면-시스템은-입력-필드를-깜빡이고-입력을-거부한다">4. 사용자가 숫자가 아닌 글자를 숫자 필드에 입력하면 시스템은 입력 필드를 깜빡이고 입력을 거부한다.</h4>
<ul>
<li><p>📖 책의 해답</p>
<ul>
<li>밑에 숨겨진 요구 사항은 아마 “시스템은 사용자가 필드에 올바르지 않은 값을 입력하는 것을 막는다. 올바르지 않은 값을 입력하는 경우 경고를 보낸다.”라는 문장에 더 가까울 것이다.</li>
</ul>
</li>
<li><p>💡 나의 해답</p>
<ul>
<li>입력을 거부할 때 어떤 방식으로 거부가 필요할지 확인이 필요하다.<ul>
<li>알림창 또는 팝업 조회</li>
<li>입력 필드 하단에 문구 표시</li>
<li>별도 표시 없이 숫자에 한하여 입력받도록 처리</li>
</ul>
</li>
</ul>
</li>
</ul>
<br/>

<h4 id="5-이-임베디드-애플리케이션의-코드와-데이터-크기는-32mb-이내여야-한다">5. 이 임베디드 애플리케이션의 코드와 데이터 크기는 32Mb 이내여야 한다.</h4>
<ul>
<li><p>📖 책의 해답</p>
<ul>
<li>이 문장은 하드웨어의 규격에 맞춘 것 같아 보인다. 아마 꼭 지켜야 하는 요구 사항일 것이다.</li>
</ul>
</li>
<li><p>💡 나의 해답</p>
<ul>
<li>아래 내용에 대하여 추가적으로 확인이 필요하다.<ul>
<li>각 소스 파일별 크기가 32MB 이하여야 하는지</li>
<li>용량 제한이 필요한 데이터의 기준이 무엇인지에 대한 정의</li>
</ul>
</li>
</ul>
</li>
</ul>
<br/>

]]></description>
        </item>
        <item>
            <title><![CDATA[[실용주의 프로그래머] 8장. 프로젝트 전에]]></title>
            <link>https://velog.io/@be_have98/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-8%EC%9E%A5-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%A0%84%EC%97%90</link>
            <guid>https://velog.io/@be_have98/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-8%EC%9E%A5-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%A0%84%EC%97%90</guid>
            <pubDate>Sun, 03 Apr 2022 12:05:04 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h2 id="3-주차">3 주차</h2>
</blockquote>
<h3 id="일--assignment-13">일 | Assignment #13</h3>
<ul>
<li>📚 8장. 프로젝트 전에</li>
<li>✔️ TIL</li>
</ul>
<br/>  

<h2 id="8장-프로젝트-전에">8장. 프로젝트 전에</h2>
<hr/>

<h3 id="📘-책에서-기억하고-싶은-내용">📘 책에서 기억하고 싶은 내용</h3>
<ul>
<li><p><strong>요구 사항의 구렁텅이</strong> (p.350)</p>
<ul>
<li>요구사항은 피드백을 반복하며 알게 된다.</li>
<li>정책은 메타데이터다. 현재의 정책 정보는 시스템이 지원하는 것들 중 한 사례일 분이고, 시스템은 다양한 정책을 처리할 수 있도록 일반적으로 구현해야 한다.</li>
<li>요구 사항 문서는 개발자의 계획을 위해서 쓰는 것이다.</li>
<li>좋은 요구 사항은 추상적이다. 사업에 필요한 사항을 정확히 반영하는 가장 간단한 표현이 좋다.</li>
<li>요구 사항이 슬금슬금 추가되는 것을 어떻게 막을 수 있을까? 
→ 반복 주기를 거치며 의뢰인과 정기적으로 피드백을 주고 받는다. (&quot;딱 한 기능만 더&quot;란 요청이 미치는 영향을 알도록)</li>
<li>프로젝트 용어 사전을 만들고 관리하라.<ul>
<li>프로젝트에서 사용하는 모든 용어와 어휘를 모아 놓은 단 하나의 장소여야 하고, 온라인 문서가 적합하다.</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>불가능한 퍼즐 풀기</strong> (p.362)</p>
<ul>
<li>그럴싸해 보이는 제약 조건이 사실은 전혀 제약 조건이 아닐 수도 있다.</li>
<li>생각의 틀을 벗어나지 말고, 틀을 찾아라. (틀 = 제약 조건)</li>
<li>불가능한 문제처럼 보인다면 잠시 딴짓을 하거나 동료와 이야기를 통해 해결을 시도해보라.</li>
</ul>
</li>
<li><p><strong>함께 일하기</strong> (p.367)</p>
<ul>
<li><strong>짝 프로그래밍</strong> : 개발자 한 명은 키보드를 조작하지만 다른 한 명은 하지 않으며, 키보드 담당은 필요에 따라 바꿀 수 있고 둘이 함께 문제를 푼다.</li>
<li><strong>몹 프로그래밍</strong> : 셋 이상의 사람이 참여하는 짝 프로그래밍의 확장 방안, 실시간 코딩을 곁들인 밀접한 협업<ul>
<li>세 명 이상의 사람은 반드시 개발자가 아니어도 된다. (테스트, 고객 모두 가능)</li>
</ul>
</li>
<li><strong>유의사항 및 TIP</strong><ul>
<li>누가 가장 똑똑한지 겨루는 것이 아니다.</li>
<li>코드만 비판하고 사람을 비판하지 말라.</li>
<li>다른 사람의 관점을 듣고 이해하려고 노력하라.</li>
<li>자주 회고를 하라.</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>애자일의 핵심</strong> (p.372)</p>
<ul>
<li>애자일 프로세스라는 것은 있을 수 없다. 애자일의 가치는 할 일을 결정할 떄 무엇을 추구해야 할지를 알려준다.</li>
<li><strong>애자일의 가치</strong><ul>
<li>공정과 도구 &lt; 개인과 상호작용</li>
<li>포괄적인 문서 &lt; 작동하는 소프트웨어</li>
<li>계약 협상 &lt; 고객과의 협력</li>
<li>계획을 따르기 &lt; 변화에 대응하기</li>
</ul>
</li>
<li><strong>애자일하게 일하기</strong><ul>
<li>아래 과정을 프로젝트 종료 시까지 반복하라.<ul>
<li>내가 어디에 있는지 알아내라.</li>
<li>도달하고 싶은 곳을 향하여 의미 있는 발걸음을 가능한 한 작게 옮겨라.</li>
<li>어디에 도착했는지 평가하고, 망가트린 것이 있으면 고쳐라.</li>
</ul>
</li>
</ul>
</li>
<li>지속적으로 프로세스를 실험하지 않는 팀은 애자일 팀이 아니다.</li>
<li>애자일이 전반적으로 작동하게 하려면 무언가를 바꾸기 쉽도록 좋은 설계를 만들어야 한다.
바꾸기 쉽다면 모든 층위에서 아무런 주저 없이 문제를 바로 잡을 수 있을 것이다.</li>
</ul>
</li>
</ul>
<br/>

<h3 id="🤔-소감-및-생각">🤔 소감 및 생각</h3>
<ul>
<li>요구 사항이 추가되는 것을 막을 수 있는 방법을 논하는 내용을 읽을 때 많이 공감되면서도 씁쓸했다. 고객은 늘 요구 사항을 한 번에 결정하지 못하고, 지속적으로 &quot;슬그머니&quot; 추가하였는데, 책에서 말하는 답과 같이 피드백을 통해 관리하려고 하였으나 결국엔 &quot;기간을 넉넉하게 드릴테니 그때까지라도 부탁드려요&quot;라는 말이 돌아왔었다. 내가 속했던 프로젝트 팀의 대부분은 결국 기간을 연장하여 모든 요구사항을 수용하는 방향으로 결정하였는데, 이러한 상황에서 요구 사항을 줄일 다른 방법이 있는지 다시 생각해보게 되었다.</li>
</ul>
<br/> 

<h3 id="🔍-새롭게-또는-다시-알게-된-내용">🔍 새롭게 또는 다시 알게 된 내용</h3>
<ul>
<li><del><em>NONE</em></del></li>
</ul>
<br/>
]]></description>
        </item>
        <item>
            <title><![CDATA[[실용주의 프로그래머] 7장. 코딩하는 동안]]></title>
            <link>https://velog.io/@be_have98/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-7%EC%9E%A5-%EC%BD%94%EB%94%A9%ED%95%98%EB%8A%94-%EB%8F%99%EC%95%88</link>
            <guid>https://velog.io/@be_have98/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-7%EC%9E%A5-%EC%BD%94%EB%94%A9%ED%95%98%EB%8A%94-%EB%8F%99%EC%95%88</guid>
            <pubDate>Sat, 02 Apr 2022 16:48:32 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h2 id="3-주차">3 주차</h2>
</blockquote>
<h3 id="금-토--assignment-12">금, 토 | Assignment #12</h3>
<ul>
<li>📚 7장. 코딩하는 동안</li>
<li>✔️ TIL</li>
</ul>
<br/>  

<h2 id="7장-코딩하는-동안">7장. 코딩하는 동안</h2>
<hr/>

<h3 id="📘-책에서-기억하고-싶은-내용">📘 책에서 기억하고 싶은 내용</h3>
<ul>
<li><p><strong>개요</strong> (p.273)</p>
<ul>
<li><span style="background-color: #FAF4C0; color: black;">프로젝트가 실패하는 가장 큰 원인은 설계 내용을 컴퓨터가 실행할 수 있는 문장으로 바꾸는 일만 하면 된다는 생각이다.<span></li>
<li>적극적으로 자기 코드에 대해 생각하지 않는 프로그래머는 우연에 맡기는 프로그래밍을 하는 것이다.</li>
</ul>
</li>
<li><p><strong>파충류의 뇌에 귀 기울이기</strong> (p.275)</p>
<ul>
<li>파충류의 뇌 = 동물적인 본능</li>
<li>코딩이 평소와 다르게 오르막길과도 같다면 지금 하는 작업이 필요 이상으로 어떤 원인으로 인하여 힘들어지고 있는 것일수도 있다.</li>
<li>휴식 및 동료에게 설명하는 방법으로 해결되지 않는다면 프로토타이핑을 시도해봐라.<ul>
<li>이때, 꺼림칙했던 느낌이 코딩 도중에 갑자기 명확한 문제로 구체화되면 즉각 해결하라.</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>우연에 맡기는 프로그래밍</strong> (p.282)</p>
<ul>
<li>우리는 행운과 우연한 성공에 의존하는 프로그래밍이 아닌 <code>의도적으로 하는 프로그래밍</code> 을 해야 한다.</li>
<li>왜 코드가 망가졌는지 모르는 까닭은 애초에 코드가 왜 잘 돌아갔는지도 몰랐기 때문이다.</li>
<li>문서화된 동작에만 의존하라, 그럴 수 없다면 추측을 문서로 상세히 남겨라.</li>
<li>가정하지 말라. 증명하라.</li>
<li>인터넷 검색으로 찾은 첫 번째 답에서 코드를 복사해 올 때 여러분과 동일한 상황이라고 확신하는가? 
아니면 의미는 신경쓰지 않고 그냥 따라 하는 &quot;화물 숭배&quot; 코드를 만들고 있나?</li>
<li>잘 되는 듯한 답을 찾는 것과 올바른 답을 찾는 것은 다르다.</li>
<li>확고한 사실에 근거하지 않은 가정은 어떤 프로젝트에서든 재앙의 근원이 된다.</li>
<li>코드뿐 아니라 내가 세운 가정도 테스트해 보아야 한다. 어떤 일이든 추측만 하지 말고 실제로 시험해 보라.</li>
<li>기존 코드가 앞으로 짤 코드를 지배하도록 놓아두지 말라. 
더는 적절한 코드가 아니다 싶으면 어떤 코드라도 교체할 수 있다.
<span style="color: blue;">언제나 리팩토링할 자세가 되어 있어야 한다.</span></li>
</ul>
</li>
</ul>
<ul>
<li><p><strong>알고리즘의 속도</strong> (p.291)</p>
<ul>
<li>코드의 실행 시간이 얼마나 될지 또는 메모리를 얼마나 사용할지 확실하지 않다면 직접 실행해보라.</li>
<li>정확하게 시간을 재는 일이 어렵다면 <code>코드 프로파일러</code> 를 사용하여 알고리즘이 돌아갈 때 각 단계의 실행 횟수를 센 다음 입력값 크기별 실행 횟수를 그래프로 그려 보라.</li>
<li>가장 빠른 알고리즘이 언제나 가장 좋은 알고리즘은 아니다.</li>
<li>&quot;성급한 최적화&quot;를 조심하라.
시간 투자 전 그 알고리즘이 정말로 병목인지 먼저 확인하는 것이 좋다.</li>
</ul>
</li>
<li><p><strong>리팩토링</strong> (p.300)</p>
<ul>
<li>소프트웨어 개발은 &quot;건축&quot;보다 &quot;정원 가꾸기&quot;에 가깝다. (유기적인 활동)</li>
<li>리팩토링은 잡초 제거나 갈퀴질처럼 위험하지 않은 작은 단계들을 밟는 일상 활동이다.</li>
<li>소스 코드를 이곳저곳 변경하는 것은 굉장히 고통스러운 작업일 수도 있다.
많은 개발자들이 코드에 조금 개선할 부분이 있다는 이유만으로는 다시 돌아가서 코드 열기를 주저한다.</li>
<li>일정의 압박은 리팩토링을 하지 않는 단골 핑계다.</li>
<li>일찍 리팩토링하고, 자주 리팩토링하라. (단, 리팩토링할 시간을 일정에 확실히 포함해야 한다.)</li>
<li>리팩토링의 본질은 재설계다.<ul>
<li>리팩토링과 기능 추가를 동시에 하지 말라.</li>
<li>리팩토링을 시작하기 전 테스트가 있는지 먼저 확인하라.</li>
<li>단계를 작게 나누어서 신중하게 작업하라.</li>
</ul>
</li>
<li>기대하는 수준에 못 미치는 코드를 발견하면, 고쳐라. 고통을 관리하라.
지금은 고통스러울지라도 앞으로 더욱 고통스러워질 것 같으면 지금 고치는 편이 낫다.</li>
</ul>
</li>
<li><p><strong>테스트로 코딩하기</strong> (p.307)</p>
<ul>
<li>테스트는 버그를 찾기 위한 것이 아니다.</li>
<li>테스트가 코드의 첫 번째 사용자다.</li>
<li>명백한 테스트는 개발을 이끌어 나가는 데 도움이 된다.
하지만 나아갈 때마다 목적지를 떠올리지 않으면 계속 같은 자리만 빙빙 돌게 될 수도 있다.</li>
<li>단위 테스트를 계약을 잘 지키는지 보는 테스트로 생각해야 한다.<ul>
<li>코드가 계약을 지키는지 확인</li>
<li>코드로 표현된 계약의 의미가 우리가 생각한 것과 일치하는지 여부 확인</li>
</ul>
</li>
<li>테스트할 수 있도록 설계하라.</li>
<li>로그 메시지는 반드시 규칙적이고 일고나된 형식이어야 한다. 
프로그램의 처리 시간이나 프로그램이 택한 논리 경로를 추론하기 위해 로그를 자동으로 파싱하고 싶을 때가 있기 때문이다.</li>
<li>테스트 코드를 다른 제품 코드와 마찬가지로 다뤄라. 결합도를 낮추고, 깨끗하고 견고하게 유지하라.</li>
<li>테스트, 설계, 코딩, 이 모든 것이 프로그래밍이다.</li>
</ul>
</li>
<li><p><strong>속성 기반 테스트</strong> (p.321)</p>
<ul>
<li><strong>속성</strong> : 코드에 존재하는 계약과 불변식</li>
<li><strong>속성 기반 테스트</strong> : 코드에서 속성을 찾아내 자동화한 테스트</li>
<li><strong>역할</strong><ul>
<li>문제가 발생하는 상황에 집중할 수 있게 해줌</li>
<li>회귀 테스트</li>
</ul>
</li>
<li>속성 기반 테스트가 실패했다면 테스트 함수가 어떤 매개 변수를 사용했는지 알아낸 다음 그 값을 이용하여 별도의 단위 테스트를 정식으로 추가하는 것이 좋다.</li>
<li>코드를 불변식과 계약이라는 관점으로 보면 데이터의 일관성을 해치는 함수를 찾기 수월해진다.</li>
</ul>
</li>
<li><p><strong>바깥에서는 안전에 주의하라</strong> (p.331)</p>
<ul>
<li>해킹은 공격자의 지능이 높아서 일어나기보다 개발자의 부주의로 벌어진다.</li>
<li><strong>보안 원칙</strong><ul>
<li>공격 표면을 최소화하라. (공격 표면 = 사용자와 서비스의 모든 접근 지점)</li>
<li>최소 권한 원칙 (최소한의 권한만을 꼭 필요한 시간만큼만 제일 짧게 부여)</li>
<li>안전한 기본값</li>
<li>민감 정보를 암호화하라</li>
<li>보안 업데이트를 적용하라</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>이름 짓기</strong> (p.341)</p>
<ul>
<li>언어 자체의 문법에선 어느 쪽이든 상관없다고 하더라도 아무 것이나 쓰면 안 된다.</li>
<li>프로그래밍 언어별 문화를 존중하라. (변수명, 표기법 등)</li>
<li>반드시 팀의 모든 살마이 각 단어의 뜻을 알고 일관성 있게 사용해야 한다.<ul>
<li>짝 프로그래밍, 용어 사전 등의 방법을 통해 의사소통을 장려할 수 있다.</li>
</ul>
</li>
<li>잘못된 이름은 바로 고쳐라, 고칠 수 없다면 ETC 위반이다.</li>
</ul>
</li>
</ul>
<br/>

<h3 id="🤔-소감-및-생각">🤔 소감 및 생각</h3>
<ul>
<li>이 챕터의 내용처럼 &#39;잘 되는 코드를 괜히 내가 리팩토링 하려고 했다가 더 오류가 나진 않을까?&#39; 라는 생각때문에 더 나은 코드로 교체하기를 두려워했던 적이 많았고, 지금도 그렇다. 새로운 시스템을 개발할 때 TDD 기반으로 진행하여야 회귀테스트 등 리팩토링 후에도 동일하게 정상 동작함을 증명 가능하기 때문에 테스트 코드 작성이 더욱 더 중요함을 다시 느끼게 되었다.</li>
</ul>
<br/> 

<h3 id="🔍-새롭게-또는-다시-알게-된-내용">🔍 새롭게 또는 다시 알게 된 내용</h3>
<ul>
<li><a href="https://ko.wikipedia.org/wiki/%EC%8A%A4%ED%8A%B8%EB%A3%A8%ED%94%84_%ED%9A%A8%EA%B3%BC">스트루프 효과</a> : 과제에 대한 반응 시간이 주의에 따라 달라지는 효과 또는 이러한 현상을 이용하는 검사</li>
</ul>
<br/>
]]></description>
        </item>
        <item>
            <title><![CDATA[[실용주의 프로그래머] 6장. 동시성]]></title>
            <link>https://velog.io/@be_have98/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-6%EC%9E%A5-%EB%8F%99%EC%8B%9C%EC%84%B1</link>
            <guid>https://velog.io/@be_have98/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-6%EC%9E%A5-%EB%8F%99%EC%8B%9C%EC%84%B1</guid>
            <pubDate>Tue, 29 Mar 2022 14:56:31 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h2 id="2-주차">2 주차</h2>
</blockquote>
<h3 id="화-수--assignment-10">화, 수 | Assignment #10</h3>
<ul>
<li>📚 6장. 동시성</li>
<li>✔️ TIL</li>
</ul>
<br/>  

<h2 id="6장-동시성">6장. 동시성</h2>
<hr/>

<h3 id="📘-책에서-기억하고-싶은-내용">📘 책에서 기억하고 싶은 내용</h3>
<ul>
<li><p>동시성 : 둘 이상의 코드 조각이 실행될 때 동시에 실행중인 것처럼 행동하는 것</p>
<ul>
<li>실행 중에 코드의 다른 부분으로 실행을 전환할 수 있는 환경에서 코드를 구동해야 함</li>
</ul>
</li>
<li><p>병렬성 : 실제로 동시에 실행되는 것</p>
<ul>
<li>두 가지 일을 동시에 할 수 있는 하드웨어가 필요함</li>
</ul>
</li>
<li><p><strong>시간적 결합 깨트리기</strong> (p.243)</p>
<ul>
<li>애플리케이션의 작업 흐름을 모델화하고 분석하는 작업이 필요함</li>
<li>활동 다이어그램 사용 시 동시에 수행할 수 있는데도 아직 동시에 하고 있지 않은 활동들을 찾아내서 병렬성 극대화 가능</li>
<li>비교적 독립적인 부분 작업들은 병렬로 처리하여 결과를 합치면 효율성을 높일 수 있음</li>
</ul>
</li>
<li><p><strong>공유 상태는 틀린 상태</strong> (p.249)</p>
<ul>
<li>코드에서 둘 이상이 파일, DB, 외부 서비스 등 어떤 리소스에 동시에 접근할 수 있다면 잠재적인 문제를 안고 있는 것</li>
<li>불규칙한 실패는 동시성 문제인 경우가 많다.</li>
</ul>
</li>
<li><p><strong>액터와 프로세스</strong> (p.258)</p>
<ul>
<li>액터 모델에서는 공유된 상태가 없기 때문에 동시성을 다루는 코드를 쓸 필요가 없다.
또한, 액터가 수신하는 메시지에 따라 알아서 실행되기 떄문에 명시적으로 처음부터 끝까지 무엇을 하라는 코드도 쓸 필요가 없다.</li>
<li>다른 대부분의 언어에도 액터 구현이 있으니, 동시에 실행되는 작업을 구현할 떄 액터를 사용하라.</li>
</ul>
</li>
<li><p><strong>칠판</strong> (p.266)</p>
<ul>
<li>칠판 : 형사가 수사 시 칠판을 이용하는 것과 같이 일종의 자유방임주의적 동시성을 의미</li>
<li>카프카나 NATS 같은 메시징 시스템은 칠판으로도, 여러 액터를 실행하는 플랫폼으로도 사용할 수 있다.<ul>
<li>이벤트 로그의 형태로 영속성을 제공하고, 패턴 매칭 형태로 메시지를 가져오는 것도 지원함</li>
</ul>
</li>
<li>아키텍처에서 MSA, 액터, 칠판 등을 사용하면 애플리케이션에서 생길 수 있는 모든 종류의 동시성 문제를 예방할 수 있지만 비용이 수반된다. 이때는 메시지 형식 및 API를 모아두는 중앙 저장소를 운영하면 도움이 될 것이다.<ul>
<li>맞춰야 하는 구성 요소 수가 더 많기 때문에 배포 및 관리가 까다롭지만 잘게 쪼개진 시스템 덕에 개별 액터만 교체하여 업데이트가 가능하게 한다.</li>
</ul>
</li>
<li>특정한 비즈니스 작업 처리를 시작할 때 고유한 추적 아이디를 만들어 붙이고, 추후 해당 작업에 관여하는 모든 액터로 아이디를 전파하면 로그를 분석하여 일어난 일을 재구성해볼 수 있다.</li>
</ul>
</li>
</ul>
<br/>

<h3 id="🤔-소감-및-생각">🤔 소감 및 생각</h3>
<ul>
<li>부끄럽지만 아직 메시징 시스템을 제대로 사용해본 적이 없는데, 추후 따로 공부해서 동시성을 고려하는 시스템을 잘 개발할 수 있도록 해야겠다고 생각했다.</li>
<li>공부는 하면 할수록 내가 얼마나 부족한 사람인지 증명하는 것만 같다. 😢</li>
</ul>
<br/> 

<h3 id="🔍-새롭게-또는-다시-알게-된-내용">🔍 새롭게 또는 다시 알게 된 내용</h3>
<ul>
<li>파이버 : 스레드보다 더 가벼운 실행 흐름을 만드는 도구</li>
</ul>
<br/>
]]></description>
        </item>
        <item>
            <title><![CDATA[[실용주의 프로그래머] 5장. 구부러지거나 부러지거나]]></title>
            <link>https://velog.io/@be_have98/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-5%EC%9E%A5-%EA%B5%AC%EB%B6%80%EB%9F%AC%EC%A7%80%EA%B1%B0%EB%82%98-%EB%B6%80%EB%9F%AC%EC%A7%80%EA%B1%B0%EB%82%98</link>
            <guid>https://velog.io/@be_have98/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-5%EC%9E%A5-%EA%B5%AC%EB%B6%80%EB%9F%AC%EC%A7%80%EA%B1%B0%EB%82%98-%EB%B6%80%EB%9F%AC%EC%A7%80%EA%B1%B0%EB%82%98</guid>
            <pubDate>Sat, 26 Mar 2022 15:23:27 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h2 id="2-주차">2 주차</h2>
</blockquote>
<h3 id="토--assignment-08">토 | Assignment #08</h3>
<ul>
<li>📚 5장. 구부러지거나 부러지거나</li>
<li>✔️ TIL</li>
</ul>
<br/>  

<h2 id="5장-구부러지거나-부러지거나">5장. 구부러지거나 부러지거나</h2>
<hr/>

<h3 id="📘-책에서-기억하고-싶은-내용">📘 책에서 기억하고 싶은 내용</h3>
<ul>
<li><p><strong>결합도 줄이기</strong> (p.182)</p>
<ul>
<li><em>우리가 어떤 것 하나만을 골라내려고 해도, 그거싱 우주의 다른 모든 것과 얽혀 있음을 깨닫게 된다. - 존 뮤어</em></li>
<li>열차 사고 : 모든 메소드나 속성들이 서로 연결되어 있는 상황</li>
<li><strong>디미터(LoD) 법칙</strong><ul>
<li>클래스 C에 정의된 메소드가 다음 목록에 속하는 것만 사용 가능<ul>
<li>C의 다른 인스턴스 메소드</li>
<li>메소드의 매개 변수</li>
<li>스택이나 힙에 자신이 생성하는 객체의 메소드</li>
<li>전역 변수</li>
</ul>
</li>
<li>현재는 의미가 많이 퇴색되었으나 기반 원칙 자체는 유효하다. </li>
</ul>
</li>
<li>전역 변수를 추가하는 것은 전체 메소드에 매개 변수를 하나씩 추가하는 것과 같다.</li>
<li>싱글톤 객체, 외부 리스소를 포함한 전역 변수 및 데이터는 결합도를 높인다. </li>
<li>상속도 결국 결합도를 높이는 것과 같다.</li>
</ul>
</li>
<li><p><strong>실세계를 갖고 저글링하기</strong> (p.193)</p>
<ul>
<li>이벤트 : 무언가 (새로운)정보가 있다는 것을 의미</li>
<li><strong>이벤트 기반 애플리케이션 개발 전략</strong><ul>
<li>유한 상태 기계<ul>
<li>상태 기계는 이벤트를 어떻게 처리할지 정의한 명세, 이벤트와 관련된 모든 문제를 해결하지는 못한다.</li>
</ul>
</li>
<li>감시자(Observer) 패턴<ul>
<li>이벤트를 발생시키는 &quot;감시 대상&quot;과 이벤트에 관심이 있는 클라이언트인 &quot;감시자&quot;로 이루어진다.</li>
<li>모든 감시자가 감시 대상에 등록되어야 하기 때문에 결합이 생긴다.</li>
<li>또한, 감시 대상이 콜백을 직접 호출하도록 구현하기 때문에 이 부분이 성능 병목이 될 수 있다.</li>
<li>감시자 패턴의 문제는 &quot;게시-구독&quot;으로 해결한다.</li>
</ul>
</li>
<li>게시-구독<ul>
<li>감시자 패턴을 일반화한 것으로, 감시자 모델의 문제(결합도, 성능)를 해결한다.</li>
<li>게시-구독 모델을 많이 사용하는 시스템의 경우 현재 어떤 일이 벌어지고 있는지 파악하기가 힘들다는 것이 단점이다.</li>
</ul>
</li>
<li>반응형 프로그래밍과 스트림</li>
<li>이벤트를 중심으로 공들여 만든 코드는 일직선으로 수행되는 코드보다 더 잘 반응하고 결합도가 낮다.</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>변환 프로그래밍</strong> (p.207)</p>
<ul>
<li>언어에서 파이프라인을 지원하지 않을 경우 아래와 같이 사용 가능하다.<pre><code class="language-javascript">    const content = File.read(file_name);
     const lines = find_matching_lines(content, pattern);
    const result = truncate_lines(lines);</code></pre>
</li>
<li>변환 사이에 값을 절대 날것으로 넘기지 않고, Wrapper 역할을 하는 자료 구조나 타입으로 값을 싸서 넘겨야 한다.</li>
</ul>
</li>
<li><p><strong>상속세</strong> (p.224)</p>
<ul>
<li>상속은 <code>타입을 조합하는 방법이라고 설명하는 시뮬라 방식</code> 은 <code>C++</code> 나 <code>Java</code> 같은 언어가 계승했다.</li>
<li>상속은 <code>동작을 다양하게 구성하는 방법이라고 설명하는 스몰토크 방식</code> 은 <code>Ruby</code> 나 <code>JavaScript</code> 에서 찾아볼 수 있다.</li>
<li>상속도 일종의 결합이고, 다중 상속은 더 심한 문제를 일으킨다.</li>
<li><strong>상속을 대체하는 방법</strong><ul>
<li>인터페이스와 프로토콜<ul>
<li>이들을 타입으로 사용할 수도 있고, 해당 인터페이스를 구현하는 클래스라면 무엇이든 그 타입과 호환되기 때문에 강력하다.</li>
<li>다형성은 인터페이스로 표현하는 것이 좋다.</li>
</ul>
</li>
<li>위임(Delegation)<ul>
<li>멤버변수에 실제 상속받아 구현하려 했던 객체를 담고, 이를 사용</li>
</ul>
</li>
<li>믹스인과 트레이트<ul>
<li>바깥세상에 노출해야 하는 메소드들을 일일이 위임해야 하는 위임의 단점을 보완한 방안</li>
<li>플래그를 이용하여 모든 검증 상황을 관리하는 것이 아닌 믹스인을 사용하여 각 상황에 맞는 전문호된 클래스를 만드는 것이 더 낫다.</li>
</ul>
</li>
</ul>
</li>
<li>상속이 답인 경우는 드물다.</li>
</ul>
</li>
<li><p><strong>설정</strong> (p.236)</p>
<ul>
<li>외부 설정으로 애플리케이션을 조정할 수 있게 하라.<ul>
<li>인증 정보, 로그 레벨과 저장 위치, 포트/IP, 라이선스 키 등</li>
</ul>
</li>
<li>설정 정보를 전역에서 접근할 수 있도록 하는 것이 아닌 API 뒤로 숨기는 서비스형 설정을 사용하라.</li>
<li>설정 정보를 바꾸기 위해 코드 빌드가 필요해서는 안 된다.</li>
<li>외부 설정을 사용하지 않는다면 코드는 적응성이나 유연성을 어느 정도 포기해야만 한다.</li>
<li>나의 프로젝트가, 나의 경력이 도도의 전철을 밟지 않도록 하라.</li>
</ul>
</li>
</ul>
<br/>

<h3 id="🤔-소감-및-생각">🤔 소감 및 생각</h3>
<ul>
<li>이번 챕터를 읽으며 반응형 프로그래밍을 처음 배울 때 책에서 말하는 <code>데이터를 거대한 강으로, 흐름으로 생각하라</code> 라는 개념이 쉽지 않았었던 기억이 났다<del><em>(지금도 사실 쉽지 않다)</em></del>. 그 당시에는 개념은 이해가 되어도 막상 코드를 작성하려니 막막하였고, 러닝커브가 크다는 사실에 잠깐 절망에 빠지기도 했다. 하지만 적응하며 답답했던 시기가 지나고 나니 반응형 프로그래밍의 장점을 점점 이해하게 됐는데, 이처럼 새로운 개념에 지속적으로 적응하려는 노력을 해야겠다고 생각하였다.</li>
<li><code>WebFilter</code> 등 일부 인터페이스 및 클래스를 상속받아 커스터마이징에 사용할 때 상속이 당연하다고 생각했었는데, 이번 챕터를 읽으며 그 생각이 바뀌게 되는 계기가 되었다. 앞으로 클래스 상속 전에 다른 방안도 고려해볼 수 있도록 노력해야겠다.</li>
</ul>
<br/> 

<h3 id="🔍-새롭게-또는-다시-알게-된-내용">🔍 새롭게 또는 다시 알게 된 내용</h3>
<ul>
<li>애너그램 : 단어의 철자 순서를 바꾸어서 만든 다른 단어</li>
<li><a href="https://ko.wikipedia.org/wiki/%EB%AF%B9%EC%8A%A4%EC%9D%B8">믹스인</a> : 다른 클래스의 부모클래스가 되지 않으면서 다른 클래스에서 사용할 수 있는 메소드를 포함하는 클래스</li>
</ul>
<br/>
]]></description>
        </item>
        <item>
            <title><![CDATA[[실용주의 프로그래머] 4장. 실용주의 편집증]]></title>
            <link>https://velog.io/@be_have98/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-4%EC%9E%A5-%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%8E%B8%EC%A7%91%EC%A6%9D</link>
            <guid>https://velog.io/@be_have98/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-4%EC%9E%A5-%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%8E%B8%EC%A7%91%EC%A6%9D</guid>
            <pubDate>Thu, 24 Mar 2022 13:27:09 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h2 id="1-주차">1 주차</h2>
</blockquote>
<h3 id="목--assignment-06">목 | Assignment #06</h3>
<ul>
<li>📚 4장. 실용주의 편집증</li>
<li>✔️ TIL</li>
</ul>
<br/>  

<h2 id="4장-실용주의-편집증">4장. 실용주의 편집증</h2>
<hr/>

<h3 id="📘-책에서-기억하고-싶은-내용">📘 책에서 기억하고 싶은 내용</h3>
<ul>
<li><p>완벽한 소프트웨어는 만들 수 없고, 이를 인정하자</p>
</li>
<li><p>실용주의 프로그래머는 자기 자신도 완벽한 코드를 작성할 수 없음을 알기 때문에 자신의 실수에 대한 방어책을 마련한다.</p>
</li>
<li><p><strong>계약에 의한 설계</strong> (p.147)</p>
<blockquote>
<h4 id="루틴과-호출자-코드-간의-계약">루틴과 호출자 코드 간의 계약</h4>
</blockquote>
<ul>
<li>만약 호출자가 루틴의 모든 선행 조건을 충족한다면 해당 루틴은 종료 시 모든 후행 조건과 불변식이 참이되는 것을 보장한다.</li>
<li>어느 한 쪽이라도 계약 내용을 지키지 못할 경우 양측이 동의한 내용에 따라 해결 방안이 실행된다.</li>
</ul>
<ul>
<li><p><code>DBC(Design By Contract)</code> : 프로그램의 정확성을 보장하기 위해 소프트웨어 모듈의 관리와 책임을 문서화하고 합의하는 데에 초점을 맞추며, 문서화하고 검증하는 것이 핵심</p>
<ul>
<li>선행 조건 : 루틴 요구사항, 루틴이 호출되기 전에 참이어야 하는 것</li>
<li>후행 조건 : 루틴이 완료되었을 때의 상태, 루틴이 할 것이라고 보장하는 것</li>
<li>클래스 불변식 : 루틴 종료 후 불변속성 보장</li>
</ul>
</li>
<li><p>게으름뱅이 코드 : 시작하기 전에 자신이 수용할 것은 엄격하게 확인하고, 내어 줄 것에 대해서는 최소한도를 약속하는 것</p>
</li>
<li><p>의미론적 불변식 : 어겨서는 안 되는 고정된 규칙인 요구 사항 표현 고정된 규칙과 변동 가능성이 큰 정책을 혼동하지 말아야 함</p>
</li>
<li><p>TDD를 하고 있음에도 DBC는 필요하다.</p>
</li>
<li><p>코드 작성 전 유효 입력 범위, 경계 조건, 선후행 조건 등을 나열하는 것만으로도 구현에 도움이 되며, 결국 DBC는 설계 기법이다.</p>
</li>
<li><p>선행 조건은 명시적으로 검증해야 할 매개 변수가 하나라도 있다면 호출자에서 검증을 수행해야 한다.</p>
</li>
</ul>
</li>
<li><p><strong>죽은 프로그램은 거짓말을 하지 않는다</strong> (p.158)</p>
<ul>
<li><code>그런 일은 절대 일어날 리 없어.</code> 라는 사고에 빠지기는 쉽다.</li>
<li>모든 오류는 정보를 준다.</li>
<li>죽은 프로그램이 끼치는 피해는 이상한 상태의 프로그램이 끼치는 피해보다 훨씬 적다.</li>
</ul>
</li>
<li><p><strong>단정적 프로그래밍</strong> (p.162)</p>
<ul>
<li>진짜 오류 처리를 해야 하는 곳에 단정을 대신 사용하지 말라.</li>
<li>단정문에 대한 오해<ul>
<li>성능 저하</li>
<li>버그가 있을 때만 단정 검사가 실패함</li>
<li>릴리즈 이후에는 더 이상 단정이 필요하지 않음</li>
<li>단정문은 디버깅 도구임</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>리소스 사용의 균형</strong> (p.167)</p>
<ul>
<li>자신이 시작한 것은 자신이 끝내라 (내가 자원을 사용하기 시작했음 내가 해제해야 함)</li>
<li><code>try-with-resouce</code> 과 같은 구문을 사용할 수 있다명  해당 범위에서만 자원이 사용되도록 해야 한다.</li>
<li>리소스를 할당한 순서의 역순으로 해제하라. (참조가 망가질 경우를 대비)</li>
<li>코드의 여러 곳에서 동일한 구성의 리소스들을 할당하는 경우에는 언제나 같은 순서로 할당해야 교착 가능성을 줄일 수 있다.</li>
<li>리소스를 할당하는 것이 언제나 그 리소스를 해제할 책임까지 져야 한다.</li>
<li>리소스를 클래스 안에 캡슐화 하는 것이 유리하다. (GC가 수행되며 리소스가 해제됨)</li>
<li><strong>자료구조에서 최상위 구조의 메모리 할당 해제 방안</strong><ul>
<li>최상위 구조가 하위 구조들을 해제할 책임을 지고, 하위 구조가 다시 재귀적으로 해제할 책임을 짐</li>
<li>최상위 구조만 할당 해제, 최상위 구조가 참조하던 하위 구조들은 연결이 끊어져 다른 곳에서 참조하지 않으면 사용되지 않음</li>
<li>최상위 구조가 하나라도 하위 구조를 가지고 있으면 자신의 할당 해제를 거부함</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>헤드라이트를 앞서가지 말라</strong> (p.177)</p>
<ul>
<li>더 진행하기 전에 피드백을 확인하고 조정하라.</li>
<li>볼 수 있는 미래까지만 고려하라.</li>
<li>미래가 어떤 모습일지 더 많이 예측하려 할수록 틀릴 가능성은 계속 높아질 것이다.</li>
</ul>
</li>
</ul>
<br/>

<h3 id="🤔-소감-및-생각">🤔 소감 및 생각</h3>
<ul>
<li><code>DBC</code> 는 처음 접해보는 개념이었는데 <code>DBC</code> 가 가지고 있는 철학이나 방법 같은 부분이 새로웠던 것 같다.</li>
<li>여러 공부를 병행하려니 체력적으로 점점 뒷받침해주지 않는 것 같다.. 체력을 키워야겠다고 생각했다.</li>
</ul>
<br/> 

<h3 id="🔍-새롭게-또는-다시-알게-된-내용">🔍 새롭게 또는 다시 알게 된 내용</h3>
<ul>
<li><a href="https://ko.wikipedia.org/wiki/%EB%B6%88%EB%B3%80%EA%B0%9D%EC%B2%B4">불변속성</a> : 어떤 객체의 상태가 프로그래머의 의도에 맞게 잘 정의되어 있다고 판단할 수 있는 기준을 제공하는 속성</li>
</ul>
<br/>
]]></description>
        </item>
        <item>
            <title><![CDATA[[실용주의 프로그래머] 3장. 기본 도구]]></title>
            <link>https://velog.io/@be_have98/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-3%EC%9E%A5-%EA%B8%B0%EB%B3%B8-%EB%8F%84%EA%B5%AC</link>
            <guid>https://velog.io/@be_have98/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-3%EC%9E%A5-%EA%B8%B0%EB%B3%B8-%EB%8F%84%EA%B5%AC</guid>
            <pubDate>Wed, 23 Mar 2022 12:35:35 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h2 id="1-주차">1 주차</h2>
</blockquote>
<h3 id="수--assignment-05">수 | Assignment #05</h3>
<ul>
<li>📚 3장. 기본 도구</li>
<li>✔️ TIL</li>
</ul>
<br/>  

<h2 id="3장-기본-도구">3장. 기본 도구</h2>
<hr/>

<h3 id="📘-책에서-기억하고-싶은-내용">📘 책에서 기억하고 싶은 내용</h3>
<ul>
<li><p><strong>일반 텍스트의 힘</strong> (p.105)</p>
<ul>
<li><code>XML</code>, <code>HTML</code>, <code>JSON</code>, <code>YAML</code> 및 <code>HTTP</code>, <code>SMTP</code>, <code>IMAP</code> 등 인터넷에서 사용되는 핵심 프로토콜도 대부분 일반 텍스트다.</li>
</ul>
</li>
<li><p><strong>셸 가지고 놀기</strong> (p.110)</p>
<ul>
<li>모든 작업을 GUI로만 하면 내가 가진 환경의 능력을 전부 이용할 수 없다.</li>
<li>GUI 환경의 기능은 일반적으로 설계자가 의도한 범위를 넘어설 수 없다.</li>
<li>색깔 조합 설정, 프롬프트 설정, 별칭과 셸 함수, 명령어 자동 완성을 통하여 개발자 편의성 증대가 가능하다.</li>
</ul>
</li>
<li><p><strong>파워 에디팅</strong> (p.114)</p>
<ul>
<li>에디터를 사용하며 특정 행위를 반복한다면 개선하고, 개선한 방법을 의식적으로 사용하도록 하라.</li>
<li>마우스 없이 키보드를 사용하는 방법을 연습하다 보면 처음엔 불편하지만 생산성을 크게 높일 수 있다.</li>
</ul>
</li>
<li><p><strong>버전 관리</strong> (p.119)</p>
<ul>
<li>공유 디렉토리, 네트워크 드라이브, 클라우드는 지속 가능한 버전 관리 시스템이 아니다. </li>
<li>혼자서 진행하는 경우나 나중에 버리기로 한 프로토타입일지라도 버전 관리가 필요하다.</li>
<li>중앙 저장소를 두면 프로젝트 업무 흐름을 원활하게 해주는 수많은 확장 기능을 이용할 수 있다.</li>
</ul>
</li>
<li><p><strong>디버깅</strong> (p.125)</p>
<ul>
<li>다른 사람의 버그를 발견하면 버그를 만들어 낸 범죄자를 비난하기보다 문제를 고치는 데에 집중해야 한다.</li>
<li>&#39;하지만 정말 그럴 리가 없는데.&#39;로 시작하는 생각의 흐름에 신경 세포 하나도 낭비하지 말라.</li>
<li>디버깅할 때 근시안의 함정에 주의하라, 실제 문제는 몇 단계 떨어져 있고 또 다른 여러 가지와 연관되어 있을 확률이 다분하다. 특정한 증상만 고치려고 하지 말고, 항상 문제의 근본적인 원인을 찾으려고 노력하라.</li>
<li>컴파일러의 경고 수준을 최고로 높게 맞춰 컴퓨터가 대신 찾아 줄 수 있는 문제를 찾느라 시간을 허비하지 않도록 한다.</li>
<li>인공적인 테스트는 애플리케이션을 충분히 테스트하지 못하기 때문에 경계 조건과 실제 최종 사용자의 사용 패턴 모두 철저히 테스트해야 한다.</li>
<li>코드를 고치기 전 실패하는 테스트부터 생성한다.</li>
<li>매우 긴 <code>Stack Trace</code>를 봐야 할 때는 이진 분할을 이용한다.<ul>
<li>중간 즈음에서 스택 프레임을 하나 골라 해당 시점에 값이 정상인지 확인하고, 아니라면 이를 다시 반복한다.</li>
<li>문제가 발생한 릴리즈 버전을 찾을 때도 이러한 방식을 사용할 수 있지만 VCS가 테스트 결과에 따라 문제가 발생한 릴리스를 찾게 할 수도 있다.</li>
</ul>
</li>
<li><code>트레이싱 구문</code> : 도달 여부 등을 알기 위해 화면 혹은 파일에 출력하는 진단용 메시지<ul>
<li>메시지를 자동으로 분석해야 할 수도 있으므로 규칙적이고 일관된 형식이어야 한다.</li>
</ul>
</li>
<li>누군가에게 코드를 설명하다 문제점을 찾게 될 수도 있다.</li>
<li><strong>단 하나</strong>만을 변경했음에도 시스템이 작동을 멈춘다면 아무 관련 없어도 변경한 그 하나에 책임이 있다.</li>
<li>놀라운 버그를 발견했을 때 고치는 것을 넘어 왜 더 일찍 발견되지 않았을지 고민해야 한다.<ul>
<li>이것과 유사한 버그가 있을 법한 다른 코드가 있는지도 확인해야 한다.</li>
</ul>
</li>
<li>버그가 누군가의 잘못된 가정으로 발생했다면 이 문제를 전체 팀과 함께 토론하라.</li>
</ul>
</li>
<li><p><strong>텍스트 처리</strong> (p.139)</p>
<ul>
<li>텍스트 처리 언어를 익혀 유틸리티, 프로토타입 등에 활용해보자. (전통적인 언어를 사용했을 때보다 시간 단축이 가능하다.)</li>
</ul>
</li>
<li><p><strong>엔지니어링 일지</strong> (p.142)</p>
<ul>
<li>파일이나 위키 말고 종이를 사용하여 엔지니어링 일지를 작성하라.<ul>
<li>진행중인 작업과 직접적인 관계가 없는 발상을 쌓아 놓을 수 있다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<br/>

<h3 id="🤔-소감-및-생각">🤔 소감 및 생각</h3>
<ul>
<li>당연하지만 지키기 어려웠던 내용도 있고, 새로운 내용도 있어 읽는 데에 지루하지 않은 것 같다. 디버깅 관련 내용이 가장 인상 깊었다.</li>
</ul>
<br/>

<h3 id="🔍-새롭게-또는-다시-알게-된-내용">🔍 새롭게 또는 다시 알게 된 내용</h3>
<ul>
<li><em><del>NONE</del></em></li>
</ul>
<br/>
]]></description>
        </item>
        <item>
            <title><![CDATA[[실용주의 프로그래머] 복습 및 미션(1)]]></title>
            <link>https://velog.io/@be_have98/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-%EB%B3%B5%EC%8A%B5-%EB%B0%8F-%EB%AF%B8%EC%85%981</link>
            <guid>https://velog.io/@be_have98/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-%EB%B3%B5%EC%8A%B5-%EB%B0%8F-%EB%AF%B8%EC%85%981</guid>
            <pubDate>Tue, 22 Mar 2022 13:45:19 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h2 id="1-주차">1 주차</h2>
</blockquote>
<h3 id="화--assignment-04">화 | Assignment #04</h3>
<ul>
<li>📚 복습</li>
<li>✔️ 미션 Mission (1) : 최애 북틸(Book-TIL) 3개 선정하기</li>
</ul>
<br/>

<h2 id="mission1">Mission(1)</h2>
<hr>

<h3 id="나의-최애-til">나의 최애 TIL</h3>
<ol>
<li><p><a href="https://github.com/shinpanda/TIL/blob/main/BOOKS/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8/TIL%232.md">https://github.com/shinpanda/TIL/blob/main/BOOKS/실용주의프로그래머/TIL%232.md</a></p>
</li>
<li><p><a href="https://nomadcoders.co/community/thread/3705">https://nomadcoders.co/community/thread/3705</a></p>
</li>
<li><p><a href="https://cindybaby.notion.site/DAY-03-04-5b811fbb19ab4683aad52b66b47e14fc">https://cindybaby.notion.site/DAY-03-04-5b811fbb19ab4683aad52b66b47e14fc</a></p>
</li>
</ol>
<br/>

<h3 id="선정-이유-및-소감">선정 이유 및 소감</h3>
<ul>
<li>세 개의 글 모두 책의 내용이 정리가 잘 되어 있고, 매일 책을 읽으신 뒤 깊이 있는 견해나 진심을 소감으로 모두 표현하신 부분이 좋았다.</li>
<li>아무래도 공개적으로 글을 작성하다 보니 소감을 매우 자유롭게 쓰지는 못하는 것 같았는데, 위 글을 보며 나도 더 자유롭게 써보아야겠다는 생각이 들었다. 그리고 같은 책을 각자 다르게 정리한 부분이 재밌었던 것 같다.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>