새소식

반응형
Java

👶주니어 자바 개발자를 위한 100가지 질문-Container, Multi-Threading

  • -
반응형

평소에 조금씩 자바 공부를 하고 있는데 기초적인 부분에 대해 너무 많이 모른다는 생각을 자주 했다. 그러던 와중 커리어리에 올라와 있는 조서희님의 주니어 자바 개발자를 위한 100가지 질문이라는 글을 보았다. 글을 보니 아직 모르는 내용도 많고 중요한 내용도 많은 것 같아 따로 정리하려고 한다.

처음 보시는 분들은 질문에 대해 먼저 고민해보고 토글을 열어 보는 것을 추천한다. 또한 답변의 정확하지 않을 수 있으므로 피드백은 환영!


📦Container

 

📌 자바 컨테이너란 무엇인가요?

더보기
자바 컨테이너란, 객체들을 저장하기 위한 저장소(객체)이다. 배열은, 원시타입의 배열을 쓸 때, 크기를 미리 선언하는데, 이는 한 번 정해지면 바꿀 수 없으므로 이에 따른 제약이 생긴다. 자바 컨테이너는 이를 해결할 수 있는데, java.util 라이브러리에 컨테이너 클래스가 있으며, List, Set, Queue, Map이 존재한다.

 

📌 Collection과 Collections의 차이는 무엇인가요?

더보기

Collection: Collection은 데이터와 자료구조를 다루는 Collection 프레임워크(List, Set, Map*)에 필요한 메서드가 선언되어 있는 인터페이스를 가리킨다. add, addAll, contains, clear 등의 자료구조와 관련된 메서드가 정의되어 있다.

*Map은 Collection을 구현하지 않았지만 Collection으로 본다.

Collections: Collections는 Arrays가 배열과 관련된 메서드를 제공하는 것처럼, 컬렉션과 관련된 메서드를 제공하는 클래스이다. fill, copy, sort등과 같은 Arrays에서 제공하는 메서드들은 물론, 멀티 쓰레드 프로그래밍에 필요한 동기화 처리가 가능하도록 제작된 Collection을 반환하는 메서드도 제공한다.

 

📌 List, Set, Map의 차이점을 말해주세요.

더보기

List: 순서가 있고 중복을 허용한다. 구현체로는 ArrayList, LinkedList 등이 있다.

Set: 순서가 없고 중복된 값을 허용하지 않는다. 순서가 없기 때문에 itorator를 사용한다. 검색속도가 빠르다.

HashSet, TreeSet 등이 있다.

Map: <key, value> 쌍의 집합으로 구성되며, key는 중복되지 않고, 순서가 없다. 순서가 없기 때문에 iterator를 사용한다. 검색속도가 빠르다. HashMap, TreeMap등이 있다.

 

📌 HashMap과 Hashtable의 차이는 무엇인가요?

더보기

HashMap

동기화가 보장되지 않는다.

검색에 뛰어난 성능을 가진다.

Key와 Value값으로 NULL을 허용한다.

 

HashTable

동기화가 보장되어 병렬프로그래밍이 가능하고 HashMap보다 처리속도가 느리다.

Key와 Value값으로 NULL을 허용하지 않는다.

 

📌 각각 어떤 상황에서 HashMap과 TreeMap을 선택하나요?

더보기

HashMap은 빠른 탐색시간을 갖는 것(O(1))이 장점이다. TreeMap은 HashMap보다는 느린(O(logN))탐색 시간을 갖지만 내부 구현으로 RedBlack Tree로 저장하여 관리하므로 key 값을 기준으로 정렬된 상태를 유지할 수 있다. 그러므로 정렬보다 빠른 탐색시간을 우선으로 한다면 HashMap을, 빠른 탐색보다 정렬된 상태를 유지해야 한다면 TreeMap을 사용하는 것이 적절하다.

 

📌 HashMap 구현 원칙은 무엇인가요?

더보기

HashMap을 구현 할 때 key는 저장된 값을 찾기 위해 사용되기 때문에 컬렉션 내에서 유일 해야한다.

Entry라는 내부 클래스를 정의하는 것이 바람직하다. 그 이유는 키와 값이 별개의 값이 아니라 서로 관련된 값이기 때문에 각각의 배열로 선언하기 보다는 하나의 클래스로 정의해서 하나의 배열로 다루는 것이 데이터의 무결성적인 측면에서 더 바람직하기 때문이다.

HashMap의 hashCode는 Object의 hashCode메서드를 사용한다. 각 주소값을 해싱하기 때문에 가장 좋은 방식이기도 하다. 하지만 String 클래스와 비슷하게 equals를 재정의 해주어야 한다면 hashCode도 재정의 해주어야 해싱을 구현한 컬렉션 클래스에서 정상적으로 동작할 것이다.

 

📌 HashSet 구현 원칙은 무엇인가요?

더보기

HashSet을 구현 할 때 Set의 인터페이스 특징대로 중복된 요소를 저장하지 않아야 한다. 저장순서를 유지하지 않는다.

HashSet의 hashCode는 Object의 hashCode메서드를 사용한다. 각 주소값을 해싱하기 때문에 가장 좋은 방식이기도 하다. 하지만 String 클래스와 비슷하게 equals를 재정의 해주어야 한다면 hashCode도 재정의 해주어야 해싱을 구현한 컬렉션 클래스에서 정상적으로 동작할 것이다.

 

오버라이딩을 통해 작성된 hashCode()가 만족해야 할 조건 3가지

1. 실행 중인 어플리케이션 내의 동일한 객체에 대해서 여러 번 hashCode()를 호출해도 동일한 int값을 반환해야한다. 하지만, 실행 시마다 동일한 int값을 반환할 필요는 없다.

2. equals메서드를 이용한 비교에 의해서 true를 얻은 두 객체에 대해 각각 hashCode()를 호출해서 얻은 결과는 반드시 같아야 한다.

3. equals메서드를 이용한 비교에 의해서 true를 얻은 두 객체에 대해 각각 hashCode()를 호출해서 얻은 결과는 반드시 같아야한다.

 

📌 ArrayList와 LinkedList의 차이점은 무엇인가요?

더보기

ArrayList:

- 자료가 쭉 늘어선 배열과 같은 형태의 구조

- 특정위치의 인덱스를 한 번에 접근이 가능하기 때문에 검색속도가 상대적으로 빠르다.

- 중간에서 삭제와 추가하는 작업이 상대적으로 느리다.

- 배열의 크기가 정해져 있으므로 한계 초과시 추가적인 연산이 필요하다.

LinkedList:

- 자료의 주소값을 연결하여 체인과 같은 형태의 구조

- 순차적으로 돌면서 검색하기 때문에 검색속도가 상대적으로 느리다.

- 중간 자료의 삭제와 추가하는 작업이 상대적으로 빠르다.

 

📌 Array에서 List로 전환하려면 어떻게 해야하나요?

더보기
class Main {
    public static void main(String[] args) {

        //불변 리스트로 만들 때, Java 9 이상 지원
        Integer[] array = {1, 2, 3};

        List<Integer> list = List.of(array);
        list.add(3); //UnsupportedOperationException 에러(불변 리스트이기 때문)
		list.set(0,1); //UnsupportedOperationException 에러(불변 리스트이기 때문)
        
		List<Integer> list2 = Arrays.asList(array);
        list2.add(3); //UnsupportedOperationException 에러(불변 리스트이기 때문)
        list2.set(0,1); //set메서드는 지원

        //기본형 배열일때, Stream활용
        int[] array3 = {1, 2, 3};
        List<Integer> list3 = IntStream.of(array3).boxed().collect(Collectors.toList());
    }
}

 

 

📌 ArrayList와 Vector의 차이점을 말해주세요.

더보기

동기화

Vector는 동기화가 가능하고 ArrayList는 동기화가 지원되지 않는 상태이다.

ArrayList에서 동기화를 사용하려면 추가적으로 코드를 추가해주어야 한다.

 

성능

ArrayList는 기본적으로 동기화 관련된 로직이 포함되어 있지 않기 때문에 벡터보다 빠른 성능을 보여준다.

 

크기증가

배열이 최대 인덱스를 초과한다면 인덱스 수가 추가되는데 Vector는 현재 배열의 100%증가, ArrayList의 경우 현재 배열 크기의 50%가 증가한다.

 

📌 Array와 ArrayList의 차이점을 말해주세요.

더보기

크기

Array의 길이는 고정이다. 하지만 ArrayList의 길이는 최대 크기가 초과되면 배열의 크기를 자동으로 증가시켜준다.

 

성능

ArrayList는 배열의 크기를 늘려줄 때 새로운 배열에 할당하기 때문에 성능 저하가 발생한다.

 

타입

ArrayList는 항상 primitive type만을 가진다.

 

📌 Queue에서, poll()과 remove()의 차이는 무엇인가요?

더보기

poll()은 만약 큐가 비어 있다면 null을 반환한다.

remove()는 큐가 비어 있다면 예외를 발생시킨다.

 

📌 thread-safe한 컬렉션 클래스들은 무엇이 있을까요?

더보기

Vector, HashTable, Stack, CopyOnWriteArrayList, CopyOnWriteArraySet, ConcurrentHashMap

 

📌 iterator란 무엇인가요?

더보기
컬렉션에서 컬렉션에 저장된 요소들을 읽어오는 방법을 표준화한 방법이다. 컬렉션에 저장된 각 요소에 접근하는 기능을 가진 Iterator인터페이스를 정의하고 Collection인터페이스에는 Itorator를 반환하는 iterator를 정의하고 있다.

 

📌 iterator의 사용 목적은 무엇인가요? 어떤 특징이 있죠?

더보기

컬렉션의 요소를 읽어오는 방법을 표준화함으로써, 코드의 재사용성을 높였다. 이는 객체지향 프로그래밍의 중요한 목적 중의 하나이다.

 

특징

remove()를 사용하려면 next()메서드를 사용한 후에 적용해야 한다.

 

📌 iterator와 listIterator의 차이는 무엇인가요?

더보기

listIterator는 iterator가 한 방향으로 요소를 읽어오는 데에 반해 양방향으로 요소를 읽고, 추가하고, 대체 시킬 수 있다.


🤹Multi-Threading

 

📌 병렬과 동시성의 차이점을 말해주세요.

더보기
병렬: 실제 여러개의 코어에서 물리적으로 동시에 실행되는 것. 실제로 한 번에 여러개씩 많은 양을 처리한다.
동시: 하나의 코어에서 여러개의 작업이 동시에 실행되고 있는 것처럼 보이는 논리적인 부분

 

📌 스레드와 프로세스의 차이를 말해주세요..

더보기
프로세스는 메모리 상에서 실행중인 프로그램을 말하며, 스레드는 이 프로세스 안에서 실행되는 흐름 단위를 말한다. 프로세스는 최소 하나의 스레드를 보유하고 있으며, 각각 별도의 주소공간을 독립적으로 할당 받는다. 스레드는 이중에 stack만 따로 할당받고 나머지 영역은 스레드끼리 서로 공유한다.

 

📌 데몬 스레드는 무엇인가요?

더보기

주 스레드의 작업을 돕는 보조적인 역할을 수행하는 스레드

주 스레드가 종료되면 데몬스레드는 강제적으로 자동 종료된다.

위의 특징 빼고는 일반 스레드와 동작이 같다.

 

📌 스레드를 만드는 방법을 나열해주세요.

더보기

1)Thread 클래스 상속 방식

  1. Thread 클래스를 상속한 클래스 정의
  2. run 메소드를 오버라이드하여 스레드 코드 작성
  3. 스레드 객체 생성하기
  4. start 메소드로 스레드 시작하기

2)Runnable 인터페이스 구현 방식 → 다른 클래스를 상속받아 써야할 때 인터페이스 구현방식을 사용한다.

  1. Runnable 인터페이스를 구현하는 클래스 정의
  2. run 메소드를 오버라이드하여 스레드 코드 작성
  3. Runnable 객체 생성하기
  4. Thread 객체 생성하기
  5. start 메소드로 스레드 시작하기

3)Runnable 또는 Callable을 구현한 후 Thread pool에 등록

 

class Main {
    public static void main(String[] args) {
        TestThread thread = new TestThread();
        thread.start();

        Runnable runnable = new TestThread2();
        Thread thread2 = new Thread(runnable);
        thread2.start();

    }
}

// Thread를 상속받아 생성하는 방법
class TestThread extends Thread {
    
    @Override
    public void run() {
        System.out.println("스레드 실행");
    } 
}

//Runnable 인터페이스를 구현하여 스레드생성하는 예제
class TestThread2 implements Runnable {

    public void run() {
        System.out.println("스레드 실행");
    }
}

 

📌 runnable과 callable의 차이는 무엇인가요?

더보기

callable은 call 메서드로 리턴 값도 줄 수 있고 Exception을 발생 시킬 수도 있다.

runnable은 Thread에 인자로 전달하여 활용할 수도 있다.

 

📌 스레드의 여러가지 상태에 대해 말해주세요.

더보기

1. ready state : start() 메서드 호출부터 스케줄러가 run() 메서드를 시작하기까지의 상태입니다.

2. run state : 스레드가 ready state이고, 스케줄러가 run() 메서드를 실행시키면 해당 스레드는    run state 상태가 됩니다.

3. non-runnable state : run state상태인 스레드가 동작을 일시 정지한 상태입니다. non-runnable state에는 3가지의 상태(wating, sleeping, blocked)가 있습니다

3-1 : wating state : synchronized 메서드나 synchronized 블록에 의해 스레드가 wait() 메서드를 호출하여 일시 정지된 상태입니다.

3-2 : sleeping state : Thread.sleep() 메서드의 실행으로 인해 일정 시간동안 동작이 정지된 상태입니다. 지정된 시간이 지나면 스레드는 ready state로 복귀합니다.

3-3 : blocked state : IO관련 상태 또는 synchronized를 통해 스레드 진행이 정지된 상태입니다. 스레드를 정지시킨 원인이 사라지면 스레드는 ready state로 복귀합니다.

4. done state : 스레드의 run()메서드가 종료된 상태입니다. 한번 done state가 된다면 그 스레드는 더이상 run state로 복귀할 수 없습니다.

 

📌 sleep()과 wait()의 차이는 무엇인가요?

더보기

sleep는 특정 시간동안 대기하는 것이며, wait는 특정 객체에 접근을 할 때 해당객체를 다른 스레드가 선점하여 락이 걸려 있을 때 대기하며 notify 또는 notifyAll로 재 동작 시킬 수 있다.

 

📌 notify()와 notifyAll()의 차이는 무엇인가요?

더보기

notify : 이 객체에 대해 대기중인 스레드 하나를 깨운다.

notifyAll : 이 객체에 대해 대기중인 모든 스레드를 깨운다.

 

notify는 임의의 스레드 하나를 깨우는 동작이므로 어떤 경우 특정 스레드 하나가 계속해서 기다리는 기아현상이 발생되기도 한다. 그러므로 해당 현상이 발생될 확률이 있는 경우 notifyAll을 통해 모든 스레드를 깨우고 해당 스레드들이 먼저 실행해도 되는 조건인지 체크한 후 실행하는 방향으로 해결한다.

 

📌 thread run()과 thread start()의 차이는 무엇인가요?

더보기
start  run
native 영역에서 새로운 Thread가 생성되며 Thread가 시작되면 run() 메서드가 실행된다. Thread가 생성되지 않으며 그냥 run() 메서드만 실행된다.
동일한 객체에서 두번이상 호출 시 IllegalThreadStateException 예외가 발생된다. 호출수에 제한없이 계속 호출할 수 있다.
멀티쓰레드로 동작한다. 싱글쓰레드로 동작한다.

 

📌 스레드 풀을 생성할 수 있는 여러가지 방법을 말해주세요.

더보기

Executers.newFixedThreadPool(int nThread)

파라미터로 제공되는 n개 만큼 스레드 풀을 생성한다. 보통 일정량의 업무가 발생할 때 사용한다.

 

Executors.newCachedThreadPool()

초기 스레드 개수가 0개로 설정되며 스레드 개수보다 많은 양의 작업이 요청되면 새로운 스레드를 생성하여 작업을 처리한다. 작업이 끝난 스레드가 60초 이상 새로운 작업요청이 없으면 스레드를 종료하고 스레드 풀에서 제거된다.

 

Excuters.newScheduledThreadPool(int corePoolSize)

스레드를 일정시간이 흐르고 난뒤 실행시키도록하는 스케줄링 스레드 기능이다.

 

📌 스레드 풀의 상태에 대해 말해주세요.

더보기

생성: 미리 스레드 풀에 많은 스레드들을 생성해둔다.

작업: 작업이 들어올 때마다 새로운 작업을 큐에 추가하고, 작업을 큐에서 하나씩 꺼내어서 적절한 쓰레드로 할당한다. 작업이 끝나면 이를 콜백형태로 작업을 요청한 주체에게 결과를 알려준다.

종료: shutdown 또는 shutdownNow, awaitTermination을 통해 종료한다.

 

📌 스레드 풀에서 submit()과 execute()의 차이는 무엇인가요?

더보기

excute는 Runnalbe만을 처리하며, 작업의 처리 결과를 반환 받을 수 없다. submit은 Runnable과 Callable 두 가지를 처리하며 작업의 처리결과를 Future라는 인터페이스로 반환한다.

excute는 쓰레드에서 작업처리 도중 예외가 발생하면 쓰레드 풀에서 해당 예외를 제거하고 새로운 쓰레드를 생성한다. submit은 작업 처리 도중 예외가 발생해도 스레드를 제거하지 않고 다음 작업에 재사용한다. submit이 오버헤드가 더 적으므로 submit을 사용하는 것이 권장된다.

 

📌 자바 프로그램에서 멀티 스레드 작업의 안전성을 어떻게 보장할 수 있을까요?

더보기

객체를 스레드에 안전하게 만들려면 변경할 수 있는 상태에 접근하는 과정을 동기화를 통해 조율해야한다. 동기화가 제대로 되어있지 않으면 객체의 데이터가 손상되는 등의 결과가 생길 수 있다.

자바에서는 synchronized키워드를 사용하여 락을 걸거나, volatile을 통해 직접 메모리에 접근하는 경우, 명시적인 락 객체, Atomic 변수를 사용하여 상황에 맞는 동기화 작업을 통해 멀티 스레드 작업의 안전성을 보장한다.

반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.