티스토리 뷰

1. Stack, Heap 메모리 차이점?


Stack : 스레드가 독립적으로 가지고 있는 영역, 메소드의 호출 정보, 지역변수 정보 보유. Stack 영역은 독립적이기 때문에 각각의 메소드가 독립적으로 호출 가능

 - PC Register, Native Method Stack도 스레드가 독립적으로 보유

Heap : 스레드가 공통적으로 사용하고 있는 공유 영역, 참조 객체 정보 보유. Method Area는 스레드가 공유해서 사용하고 있으며, 클래스로더가 바이트코드를 처음으로 

적재하는 공간으로서, 클래스와 관한 모든 정보들을 보유하고 있다. 리플렉션은 Method Area 정보를 이용해서 수행


2. sleep() 과 wait() 차이


synchronized 키워드 내부에서 sleep()은 lock은 해제하지 않고, 단지 Block 상태로 전환되지만, wait()은 lock 해제와 동시에 Block 상태로 전환

 - WAITING : wait(), sleep() 메소드로 인해 대기하고 있는 상태 (notify(), notifyAll() 메소드를 통해 RUNNABLE 상태 전환 가능)

 - TIME_WAITING : 지정된 시간 이후에 WAITING 상태로 전환이 가능한 상태, 물론 notify(), notifyAll() 메소드를 통해서도 상태 전환 가능

 - BLOCKED : monitor를 흭득하기 위해 다른 스레드가 LOCK을 해제하기를 대기하는 상태

  * monitor : 스레드 동기화와 관련된 개념으로서, 한 스레드가 동기화 영역에 들어갔을 때 나머지 스레드는 동기화 영역에서 작업하고 있는 스레드가 완료할 때까지 대기하는데,
    대기하는  
스레드들은 모두 BLOCK 상태


3. Multi Thread에서 Single 인스턴스를 사용할 때 주의할 점?


멀티 스레드에서 공유자원에 대해 접근했을 때 문제 발생 가능. 동시성 제어 전략 필요

멀티스레드는 스레드의 안전성을 보장하면서, 성능을 최대한으로 올리는 것을 목표로 한다.

멀티스레드는 lock 제어나 context switch로 인해 단일 스레드보다 속도가 느려질 수 있는 상황이 발생할 수 있다. 그렇기 때문에 멀티스레드의 가장 큰 이점은 멀티 코어 

환경에서 미사용 자원없이 최대한 활용할 수 있다는 점이다. 또한, 속도 측면에서 멀티스레드가 유리할 수 있는 환경이 존재할 수 있는데, 각 스레드의 작업이 서로에게 영향을 

미치지 않고 독립적이라 한다면, 해당 스레드에게 작업을 나눠 처리 가능하다. 그러나 해당 작업들이 서로에게 종속적이라면 멀티스레드라 해도, 단일 스레드처럼 동작할 것이다. 


 (1) synchronized 키워드 사용

 모든 객체들은 고유한 lock을 가지고 있으며, 멀티 스레드 환경에서 객체의 lock을 보유한 스레드만이 객체를 접근할 수 있도록 제어가 가능하다.

 아래의 예제는 스레드풀을 이용해서 스레드 2개를 생성해서 각 스레드들이 count 필드를 증가시키도록 구현했다. synchronized 키워드가 없다면, 정상적인 결과를 기대할 

 수 없다. 그 이유는 'count++' 은 count 값을 가져오고, 값을 더하고, 값을 메모리에 할당하는 3가지 단계를 거치는데, 스레드1이 값을 더하고 아직 할당하지 않는 상태에서 

 스레드2로 변경되면 스레드2는 이전값에 더하게 된다, 그래서 더하기 연산이 정상적으로 이루어지지 않는다. 그래서 synchronized 키워드를 통해 해당 객체에 대한 lock을 

 보유하고 있는 스레드만이 접근하게 하도록 제어가 필요한 것이다. 

           
package concurrent;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

public class Test_1 {

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);

        Counter counter = new Test_1().new Counter();

        IntStream.range(0, 10000)
                .forEach(i -> executor.submit(counter::plus));

        executor.shutdown();

        System.out.println(counter.getCount());  // 973

    }

    public class Counter {

        private int count = 0;

        public Counter() {

        }

        public void plus() {
            synchronized (this) {
                count++;
            }
        }

        public int getCount() {
            return count;
        }
    }
}


 (2) Atomic 클래스

 Atomic 클래스란 원자성을 보장하는 클래스를 의미한다. synchronized 키워드를 사용하면 해당 블락이 lock 되기 때문에 다른 스레드들은 모두 대기해야 하므로 

 자원낭비가 심하다.

  - 원자성 : 작업 중간에 실패했을 경우, 처음 상태로 원복하여 데이터의 무결성을 보장

 동기화에서 문제가 되는 것은 해당 객체 자체가 아니라, 객체 내부의 필드를 통해 문제가 발생하기 때문에 Atomic 클래스는 이 부분에서 효과적이다.

 그래서 비동기하면서도, 동기화문제를 해결하기 위한 방법이 Atomic 클래스이다. Atomic 클래스는 CAS 알고리즘 바탕으로 동작한다.




 멀티스레드 환경, 멀티 코어 환경에서 각 CPU는 메인 메모리의 값을 참조하는 것이 아닌, 각 CPU 영역의 캐시를 참조하게 된다. 메인 메모리와 CPU의 캐시의 값이 다를 

 경우 문제가 문제가 발생할 수 있는데 이를 가시성 문제라고 한다. CAS 알고리즘은 이 문제를 해결할 수 있는데, 현재 스레드의 값과 메인 메모리의 값을 비교하여 일치하지 

 않았을 때 메인 메모리의 값으로 교체하는 방식을 통해 각 CPU의 캐시에서 잘못된 값으로 조작하는 것을 방지한다. 


멀티스레드를 동기화 문제에 많은 어려움이 존재하는데, 왜 멀티스레드를 써야할까? 멀티스레드가 가지는 장점은 무엇이고 어느 상황에서 멀티스레드를 더 권장할까?

내일 알아봐야지!



공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함