jhhan의 블로그

Java의 정석을 시작하자(4)(2/7) 본문

JAVA

Java의 정석을 시작하자(4)(2/7)

jhhan000 2020. 2. 14. 10:30

13장 이후부터는 여기다 작성하는 것으로 결정했다.

13장. 쓰레드

-프로세스 & 쓰레드

  • 프로세스 : 실행 중인 프로그램
  • 프로세스 구성 : 필요한 데이터 & 메모리 등의 자원, 쓰레드
  • 쓰레드 : 실제로 작업을 수행하는 것
  • 쓰레드가 둘 이상이면 멀티쓰레드 프로세스라고 한다.
  • ex) 프로세스 = 공장 ,  쓰레드 = 일꾼 이라고 생각하면 이해하기 쉽다.

-멀티쓰레딩

장점 단점

- CPU사용률을 향상시킨다.
- 자원을 보다 효율적으로 사용 가능하다.
- 응답성이 향상된다.
- 작업 분리 -> 코드가 간결해진다.

- 자원을 공유하는 특성 존재
- 동기화(synchronization), 교착상태(deadlock) 같은 문제들을 고려하면서 프로그래밍 진행

-쓰레드의 구현 & 실행

  • Thread클래스를 상속받는다.
  • Runnable인터페이스를 구현한다.
  • 2가지 방법 모두 상관없지만 Thread클래스를 상속받으면 다른 클래스 상속이 안되기 때문에 보통은 Runnable인터페이스 구현을 한다.
  • Runnable인터페이스는 run()만 정의되어 있는 간단한 인터페이스이다.

** 쓰레드의 구현 & 실행에 대한 예제이다.

** Thread클래스와 Runnable인터페이스를 사용해서 진행했다.

** 각각의 경우 인스턴스 생성방법이 다르므로 알아두자.

 

-start()

  • 쓰레드를 생성한 후 start()를 호출해야 쓰레드가 실행된다.
  • 쓰레드 작업이 한번 더 진행되어야 한다면 새로운 쓰레드를 한번 더 생성한 다음 start() 호출해야 한다.

-start() & run()

  1. main메서드에서 쓰레드의 start() 호출
  2. start()는 새로운 쓰레드 생성, 쓰레드가 작업하는데 사용될 호출스택 생성
  3. 생성된 호출스택에 run() 호출 -> 쓰레드가 독립된 공간에서 작업 수행
  4. 호출스택이 2개 -> 스케줄러가 정한 순서에 의해 진행된다.

-싱글쓰레드 & 멀티쓰레드

  • 하나의 쓰레드로 2개의 작업을 수행한 시간 : T1
  • 2개의 쓰레드로 2개의 작업을 수행한 시간 : T2
  • T1과 T2는 거의 비슷하다.
  • 솔직히 멀티쓰레드가 context switching 때문에 좀 더 걸린다.
  • 단순히 CPU만 사용하는 계산작업은 싱글쓰레드가 더 효율적

-쓰레드의 I/O블락킹

  • 서로 다른 자원을 사용하는 작업의 경우 : 멀티쓰레드 프로세스가 더 효율적
  • ex) 사용자로부터 입력받는 작업과 화면에 출력하는 작업이 있을 때
  • 멀티쓰레드는 하나의 쓰레드가 사용자의 입력을 기다리는 동안 다른 쓰레드가 작업을 처리 가능
  • 효율적인 CPU 사용 가능

멀티쓰레드 관련 예제
입력창
결과

  • 숫자가 줄어드는 동안 아무값을 입력받을 수 있도록 했다.
  • 입력을 한다면 결과처럼 나올 것이다.

-쓰레드의 우선순위

  • 쓰레드에 우선순위를 정해 실행시간이 달라질 수 있다.
  • 범위 : 1~10
  • 숫자가 높을수록 우선순위가 높다(10 : 최대우선순위, 1 : 최소우선순위, 5 : 보통우선순위)

쓰레드의 우선순위 예제

  • 저 코드에 대한 결과는 컴퓨터마다 달라질 수 있다.
  • 자바가 OS독립적이긴 하지만, OS종속적인 부분이 몇 가지 있는데, 쓰레드가 그 중 하나이다.
  • 즉, 위의 예제는 OS마다 결과가 다르게 나온다. 
  • OS마다 다른방식으로 스케쥴링하기 때문이다.
  • 또 컴퓨터의 성능에 따라서도 다른 결과가 나올 수 있다.

-쓰레드 그룹(Thread Group)

  • 서로 관련된 쓰레드를 그룹으로 다루기 위한 것
  • 보안상의 이유로 도입된 개념
  • 그룹을 지정하지 않으면 자동으로 main쓰레드 그룹에 속하게 된다.

-데몬 쓰레드(daemon thread)

  • 일반 쓰레드의 작업을 보조하는 보조적인 역할을 수행하는 쓰레드
  • ex) 가비지 컬렉터, 자동저장, 화면자동갱신 등
  • 데몬 쓰레드가 생성한 쓰레드는 자동으로 데몬 쓰레드가 된다.

데몬 쓰레드 예제

  • setDaemon메서드는 반드시 start()를 호출하기 전에 실행되어야 한다.
  • 그렇지 않으면 IllegalThreadStateException이 발생

-쓰레드의 상태

상태 설명
NEW 쓰레드가 생성되고 아직 start()는 호출되지 않음
RUNNABLE 실행 중 또는 실행 가능 상태
BLOCKED 동기화블럭에 의해 일시정지된 상태
WAITING , TIMED_WAITING - 쓰레드의 작업은 종료X, 실행가능하지 않은 일시정지상태
- TIMED_WAITING은 일시정지시간이 지정된 경우
TERMINATED 쓰레드의 작업 종료

-쓰레드의 실행제어

  • 쓰레드의 실행을 제어하는 메서드 제공
  • 효율적인 프로그래밍 가능하도록 도와준다.
  • sleep() : 지정시간동안 멈춤
  • interrupt() : 쓰레드의 작업을 멈추라고 요청 , 강제성은 없다.
  • suspend(), resume(), stop() : 쓰레드의 실행제어를 하는 가장 손쉬운 방법
  • 하지만 교착상태를 일으키기 쉬움 -> 현재는 Deprecated됨
  • join() : 다른 쓰레드의 작업을 기다림
  • yield() : 다른 쓰레드에게 양보  -> yield() 와 interrupt()를 잘 사용하면 효율적인 실행 가능

-쓰레드의 동기화

  • 멀티쓰레드 프로세스에서 고려해야 하는 것
  • 한 쓰레드가 특정 작업을 끝마치기 전까지 다른 쓰레드에게 방해받지 않도록 하는 것 필요
  • critical section(임계 영역), lock 개념이 도입
  • synchronization(동기화) : 한 쓰레드가 진행 중인 작업을 다른 쓰레드가 간섭하지 못하도록 막는 것
  • synchronized 키워드를 이용한다.
  • 메서드를 임계영역으로 지정 OR 특정 영역을 임계영역으로 지정 -> 2가지 방식 존재
  • 임계영역에 들어가면 lock(락)을 얻어 작업을 수행 ,  벗어나면 lock(락)을 반환.

synchronized를 이용한 동기화 예제

  • synchronized 키워드를 이용하여 작성한 예제이다.
  • 주의 : balance의 접근 제어자가 private이다.
  • 그래야 값의 변경이 임의로 일어나지 않는다.

-wait() & notify()

  • sychronized로 동기화 하면 공유 데이터를 보호할 수 있다.
  • 특정 쓰레드가 락을 오랜 시간 유지하면 안된다.
  • 그래서 등장한 메서드 wait, notify
  • wait() : 쓰레드가 락을 반납하고 기다리게 한다.
  • notify() : 작업을 중단했던 쓰레드가 다시 락을 얻어 작업을 진행할 수 있게 한다.

 

14장. 람다와 스트림

-람다식(Lambda Expression)

  • 메서드를 하나의 식으로 표현한 것
  • 익명함수(anonymous function)라고도 한다.

-람다식 작성

  • 메서드에서 이름과 반환타입 제거
  • 매개변수 선언부와 {} 사이에 '->' 추가
  • 반환값이 있는 경우 : return문을 생략할 수 있다.
  • 매개변수의 타입 : 추론 가능한 경우 생략 (대부분 생략 가능)

-람다식 : 익명클래스의 객체와 동등

-함수형 인터페이스

  • 인터페이스를 통해 람다식을 다룰 수 있다.
  • 람다식을 다루기 위한 인터페이스 = 함수형 인터페이스
  • 함수형 인터페이스는 오직 하나의 추상 메서드만 정의되어 있어야 한다.

  • 함수형 인터페이스에 대한 예제이다.

-java.util.function패키지

  • 일반적으로 자주 쓰이는 형식의 메서드를 정의
  • 가능하면 이 패키지를 이용하는 것이 좋다.
함수형 인터페이스 메서드 설명
java.lang.Runnable () -> void run() -> () 매개변수 없음, 반환값 없음
Supplier<T> () -> T get() -> T 매개변수 없음, 반환값 있음
Consumer<T> T -> void accept(T t) -> () 매개변수 있음, 반환값 없음
Function<T,R> T -> R apply(T t) -> R 일반적인 함수 형태
Predicate<T> T -> boolean test(T t) -> boolean 조건식 표현할 때 사용됨
매개변수 하나, 반환타입은 boolean
BiConsumer<T,U> T,U -> void accept(T t, U u) -> () 2개의 매개변수, 반환값 없음
BiPredicate<T,U> T,U -> boolean test(T t, U u) -> boolean 조건식 표현하는데 사용
매개변수 2개, 반환타입은 boolean
BiFunction<T,U,R> T,U -> R apply(T t, U u) -> R 2개의 매개변수, 하나의 타입 반환
UnaryOperator<T> T -> T apply(T t) -> T Function의 자손
매개변수와 결과의 타입이 같다.
BinaryOperator<T> T,T -> T apply(T t, T t) -> T BiFunction의 자손
매개변수와 결과의 타입이 같다.

java.util.function 패키지 예제
결과

  • Supplier는 1~100 사이의 임의의 정수 생성
  • Consumer는 출력
  • Predicate은 조건문 - 짝수인지 확인
  • Function은 일의 자리를 없애는 식

-Predicate의 결합

  • and(), or(), negate() 로 연결할 수 있다.

  • 그에 대한 예제를 표현한 것이다.

-컬렉션 프레임웍과 함수형 인터페이스

  • 예제를 보는 것이 이해가 더 빠르다.

컬렉션 프레임웍과 함수형 인터페이스 예제
결과

-메서드 참조

  • 메서드 참조를 이용해서 람다식을 더 간략히 할 수 있다.
  • ex) Function<String, Integer> f = (String s) -> Integer.parseInt(s); 일 때
  • Function<String, Integer> f = Integer::parseInt; 로 하면 메서드 참조이다.
종류 람다 메서드 참조
static메서드 참조 (x) -> ClassName.method(x) ClassName::method
인스턴스메서드 참조 (obj x) -> obj.method(x) ClassName:method
특정 객체 인스턴스메서드 참조 (x) -> obj.method(x) obj::method

 

다음으로 스트림이 있는데 스트림은 분량이 많아서 다음으로 넘기겠다.