java 문법 종합반 5주차
학습목표
- 프로세스와 쓰레드
- 자바8 - 모던 자바 - 람다, 스트링
프로세스 vs 쓰레드
프로세스: 운영체제로부터 자원을 할당받는 작업의 단위
실행중인 프로그램
쓰레드: 프로세스가 할당받은 자원을 이용하는 실행의 단위
혼자일하면 singlw thred, 여럿이 일하면 multi thred
프로세스 구조
os가 프로그램 실행을 위한 프로세스를 할당해 줄 때 코드, 데이터, 메모리 영역(스택, 힙)을 함께 할당
- Code 는 Java main 메소드와 같은 코드를 말합니다.
- Data 는 프로그램이 실행중 저장 할 수 있는 저장공간을 의미합니다.
- 전역변수, 정적변수(static), 배열등 초기화된 데이터를 저장하는 공간
- Memory (메모리 영역)
- Stack : 지역변수, 매개변수 리턴 변수를 저장하는 공간
- Heap : 프로그램이 동적으로 필요한 변수를 저장하는 공간 (new(), mallock())
쓰레드: 프로세스 내에서 일하는 일꾼(코드실행의 흐름)
프로세스가 작업중인 프로그램에서 실행요청이 들어오면 쓰레드를 만들어 명령을 처리하도록 함 -> 자원관리 필요
프로세스 안에는 여러 쓰레드들이 있고 쓰레드 들은 실행을 위한 프로세스 내 주소공간이나 메모리 공간(Heap)을 공유 받음
쓰레드들은 각각 명령처리를 위한 자신만의 메모리공간(stack)도 할당받음
자바에서 쓰레드
일반 쓰레드와 동일 하지만 JVM 프로세스 안에서 실행됨
자바 프로그램 쓰레드는 Java Main 쓰레드부터 실행되며 JVM에 의해 실행됨
싱글 쓰레드
여태까지의 학습에서 사용한 방식
main 메서드 안에서(만)의 다양한 처리를 실행 한 것이 main 쓰레드 <- 이것만이 작동 => 싱글 쓰레드
메인 메서드만 실행시켰을 때 이것이 싱글 쓰레드
JVM의 메인 쓰레드가 종료되면, JVM도 같이 종료
멀티 쓰레드
mian 쓰레드 안에서 쓰레드를 여럿으로 나누어서 사용 -> 여러개의 쓰레드를 만들어서 동시다박 적으로 업무를 처리
자바는 멀티 쓰레드를 지원 - 병렬로 코드를 실행시킬 수 있는 조건이 됨
프로세스 안에서 여러개의 쓰레드가 실행되는 것
java 프로그램은 메인 쓰레드외의 다른 작업 쓰레드들을 생성하여 여러개의 실행흐름을 만들 수 있음 - main 쓰레드 안에서 만들어서 실행
멀티 쓰레드 장점
여러개의 작업을 동시에 할 수 있어서 성능이 좋아짐
스택을 제외한 모든 영역에서 메모리를 공유하기 때문에 자원을 보다 효율적으로 사용할 수 있음
응잡 쓰레드와 작업 쓰레드를 분리하여 빠르게 응답을 줄 수 있음(비동기)
멀티 쓰레드 단점
동기화 문제가 발생할 수 있음 - 프로세스의 자원을 공유하면서 작업을 처리하기 때문에 자원을 서로 사용하려고 하는 충돌이 발생하는 경우를 의미
교착 상태(데드락)이 발생할 수 있음 - 둘 이상의 쓰레드가 서로의 자원을 원하는 상태가 되었을 때 서로 작업이 종료되기만을 기다리며 작업을 더 이상 진행하지 못하게 되는 상태를 의미
-> 멀티 쓰레드를 안정적으로 컨트롤 가능한 다양한 기법들이 있음
구현방법 3가지
Thread class를 이용하는 것 (상속): Test 클래스를 상속받아서 run 메서드를 오버라이드 한다.
Runnable Interface를 이용하는 것 (자주 사용): Runnable 인터페이스를 implements 받아서 run 메서드를 오버라이드한다.
람다식을 이용하는 것: 람다식 -> 익명 함수처럼 사용가능 (자주 사용) / 다른 방식에서 run 메서드에 작성한 내용을 이 방식에서는 람다식 내에 작성
인스턴스화 시킨 객체 -> start() 메서드로 실행 가능
멀티쓰레드
순차적이지 않음
걸리는 시간이나 동작을 예측할 수 없음
데몬 쓰레드
보이지 않는 곳에서 실행되는 낮은 우선순위를 가진 쓰레드 -> 다른 쓰레드에 비해 리소스를 적게 할당받는다. => 느리게 실행, 데몬 쓰레드가 전부 실행될 때까지 기다리지 않고 다른 쓰레드가 전부 실행되면 메인 쓰레드가 종료됨
보조적인 역할을 담당, 대표적으로는 가비지 컬렉터가 있음
사용자 쓰레드
보이는 곳에서 실행되는 높은 우선순위를 가진 쓰레드
대표적으로 메인 쓰레드가 있음
JVM은 사용자 쓰레드의 작업이 끝나면 데몬 쓰레드도 자동으로 종료시킴
쓰레드 우선순위: 멀티 쓰레드 - 한 프로세스에 여러 개의 쓰레드 있음 -> 작업 중요도가 높을 때 우선순위를 높게 지정하면 더 많은 작업시간을 부여받아 빠르게 처리될 수 있음
최대 우선순위 (MAX_PRIORITY) = 10
보통 우선순위(NROM_PRIORITY) = 5, 기본값
최소 우선순위(MIN_PRIORITY) = 1
이 우선순위의 범위는 OS가 아니라 JVM에서 설정한 우선순위
쓰레드 그룹: 쓰레드를 특징마다 묶어 그룹으로 관리해 한꺼번에 처리
모든 쓰레드들은 반드시 하나의 그룸에 포함되어 있어야 함(모든 쓰레드들은 메인 쓰레드 밑에 속해있기 때문에 기본적으로 그룹에 포함되어 있음)
쓰레드 상태와 제어
쓰레드는 상태를 가짐
쓰레드 상태
대기상태가 되었다가 스케쥴러에 의해 실행
실행과 대기를 반복하며 run()메서드를 수행
run()메서드가 종료되면 실행이 멈춤
순환함
아래는 쓰레드의 상태를 정리한 표이다.
상태 | Enum | 설명 |
객체생성 | NEW | 쓰레드 객체 생성, 아직 start() 메서드 호출 전의 상태 |
실행대기 | RUNNABLE | 실행 상태로 언제든지 갈 수 있는 상태 |
일시정지 | WAITING | 다른 쓰레드가 통지(notify) 할 때까지 기다리는 상태 |
일시정지 | TIMED_WAITING | 주어진 시간 동안 기다리는 상태 |
일시정지 | BLOCKED | 사용하고자 하는 객체의 Lock이 풀릴 때까지 기다리는 상태 |
종료 | TERMINATED | 쓰레드의 작업이 종료된 상태 |
start(): new -> runnable
sleep(): 실행 중인 쓰레드를 일시정지 상태로 / 현재 쓰레드를 지정된 시간동안 멈추게 함 / 자기자신에 대해서만 멈추게 할 수 있음 / static 메서드 / 객체를 지칭해서 그 객체만 멈추게 하는 것이 아닌 어떤 흐름 안에서 그 쓰레드를 멈추게 함 -> 특정 쓰레드.sleep()은 의미가 없음
interrupt(): 일시정지 상태인 쓰레드(또는 실행중인 쓰레드)를 실행대기 상태로 만듦
join(): 정해진 시간동안 지정한 쓰레드가 작업하는 것을 기다림 (시간을 지정하지 않을 경우-> 지정한 쓰레드의 작업이 끝날때까지 기다림) / interrupt()를 만나면 기다리는 것을 멈추기 때문에 interruptedException이 발생할 수 있음
yeild(): 남은 시간을 다음 쓰레드에게 양보하고 쓰레드 자신은 실행대기 상태가 됨
synchronized:
- 멀티 쓰레드 -> 리소스를 공유 -> 충돌 상황 -> 장애나 버그 => 이러한 일을 방지하기 위해 한 쓰레드가 진행중인 작업을 다른 쓰레드가 침범하지 못하도록 막음 "쓰레드 동기화" /
- 임계영역: 동기화를 하려면 다른 쓰레드의 침범을 막아야하는 코드들/
- 사용방법 1 실행할 메서드 앞에 키워드 사용 2실행할 코드 묶음 앞에 키워드 사용 => 임계영역을 지정하여 다른 쓰레드의 침범을 막음
wait(), notify(): 침범을 막은 코드를 수행하다가 작업을 더 이상 진행할 상황이 아니면, wait()을 호출하여 쓰레드가 Lock을 반납하고 기다리게 할 수 있음 -> 실행 중이던 쓰레드는 대기실에서 통지를 기다림 -> 작업을 진행할 수 있는 상황이 되면 notify()로 통지
notify()는 해당 객체의 대기실에 있는 모든 쓰레드 중에서 임의의 쓰레드만 통지를 받음 -> 원하는 쓰레드에 통지 못함
Lock: synchronized 블럭으로 동기화하면 자동적으로 Lock이 걸리고 풀리지만, 같은 메서드 내에서만 Lock을 걸 수 있다는 제약이 있습니다. 이런 제약을 해결하기 위해 Lock 클래스를 사용합니다.
- reentrantLock: 재진입 가능한 lock, 가장 일반적인 배타 lock, 특정 조건에서 Lock을 풀고, 나중에 다시 Lock을 얻어 임계영역으로 진입이 가능하다.ReentrantLock을 사용하면, 같은 스레드가 이미 락을 가지고 있더라도 락을 유지하며 계속 실행할 수 있기 때문에 데드락이 발생하지 않습니다.-> 코드의 유연성을 높일 수 있음
- ReentrantReadWriteLock: 읽기를 위한 Lock과 쓰기를 위한 Lock을 따로 제공
- StampedLock: ReentrantReadWriteLock에 낙관적인 Lock의 기능을 추가했습니다 / 작업은 빠르지만 여러 쓰레드가 동시에 작업을 할 때에는 적합하지 않음
Condition: wait() & notify()의 문제 점인 waiting pool 내 쓰레드를 구분하지 못한다는 점을 해결, reentrantLock과 함께 사용
모던 자바 & 자바8
- 자바는 진화하는 언어, 가장 큰 진화는 java8에서 이루어짐
- 람다식(() -> {})은 요즘스러운 문법
- 문제가 변화 -> 프로그래밍 언어에 요구되는 기능들도 변화
- 과거: c, c++이 우세 -> 시대변화로 c#, java가 각광 -> 시장 상황에 따라 프로그래밍 언어는 대안으로 등장하기도, 도태되기도 함
- 자바: 객체지향언어의 대표주자, jvm만 있으면 어디서나 실행가능 -> 각광받음
- 새로운 요구사항이 생김
- 병렬처리
- 함수형 프로그래밍(프로그래밍의 한 패러다임) -> 람다식 등 / 프로그램을 순수한 함수의 모음으로 바라보고 구현
- 검증이 쉬움(검증이 필요한 부분만 검증할 수 있음)
- 성능 최적화가 쉬움 (캐싱-특정 인풋에 대한 아웃풋을 재사용 할 수 있음)
- 동시성 문제를 해결하기 쉬움(함수는 다른 값의 변경을 야기하지 않음)
- 수학의 함수처럼 특정한 데이터에 의존하지 않는 것 -> 순수 함수 / 외부의 어떤 영향력을 받음 -> 순수 함수가 아님
- 자바는 큰 시스템에서 거의 핵심 -> 새로운 생태계에 적응해야 할 시기 -> 자바8(함수형 프로그래밍 추가)
- 함수를 일급 값으로 (함수를 개체/ 변수처럼 사용) - 자바8에 메서드 참조 기능이 도입
- 람다: 익명함수 / 일급 객체로 취급 - 함수를 값으로 사용 할 수도 있으며 파라미터로 전달도 가능
- 스트림(<- 맵, 필터): 소스에서 추출된 연속된 요소 / 컬렉션의 반복을 처리하는 기능
- 새로운 요구사항이 생김
- 함수형 프로그래밍
- 함수를 파라미터로 받을 때 타입 -> 함수형 인터페이스: 인터페이스는 타입 역할 을 할 수 있기 때문 / 추상 메소드를 딱 하나만 가지고 있음
- 람다식: (매개변수, ...)->{로직(return문 여부에 따라 생략 가능)}
- 스트림: 자료구조들의 흐름을 객체로서 api 형태로 제공
- 원본 자료구조의 데이터를 변경하지 않으면서 새로운 값을 추출 -> map(), filter(), forEach()
- map() -> 모든 요소를 가공해서 반환
- filter() -> 조건에 맞는 것만 반환
- 모든 컬렉션을 상속하는 구현체들은 스트림을 반환 할 수 있음
- 원본 자료구조의 데이터를 변경하지 않으면서 새로운 값을 추출 -> map(), filter(), forEach()
- null
- NullPointException이 런타임 중에 많이 발생
- null을 참조하게 되는 경우 -> NullPointException발생
- null이 반환될 여지가 있으면 명시 -> null체크를 하지 않는다면 시스템은 위험 -> 객체를 감싸서 반환하기