자바 I/O & NIO 네트워크 정리 2: 스레드

1. 😘 스레드란

우리가 유투브를 키고(프로세스), MSN메신저(프로세스)키는것이 멀티 프로세스이다.
이처럼 프로세스에서도 멀티 쓰레드를 사용할수 있다.

1. 프로세스 : 자신의 주소공간을 갖는 독립적인 실행 프로그램

2. 스레드 : 프로세스 내의 독립적인 순차흐름 또는 제어

특히 자바는 언어적 차원에서 스레드를 지원하는 일반적인 언어중 하나로, JVM자체가 하나의 프로세스이기 때문에 멀티스레트 프로그램을 쉽고 명료하게 만들수 있다.

  • JSP
    JSP가 쓰레드 기반이라 프로세스 방식인 CGI,ASP,PHP 보다 가볍다는 내용을 접했을 것이다.
    대부분의 운영체제는 프로세스가 다른 프로세스의 메모리를 참조하는것이 금지되어있기때문에 파이프나 IPC를 이용하는데, 성능에 많은 저하가 있다.
    하지만 쓰레드끼리는 데이터를 공유 쉽기때문에 효율적이다.

쓰레드는 병렬처리가 가능하기 떄문에, 애플리케이션의 성능과 효율을 상승시킬수 있다.
하지만 CPU사용률이 높은 오랜시간에 걸리는 작업엔 싱글스레드로 처리하는 방식이 효과적이다.

2. 👍 스레드의 생성과 시작

1. 스레드의 생명 주기

-Thread 인스턴스를 생성
-start를 통해 인스턴스 실행
-Runnable 객체가 run()의 메소드를 실행
-run 메소드의 모든처리가 끝나면 Thread 소멸

2. white box

쓰레드의 구현 방법중 첫번째 방법은 상속을 이용하는것이다.
Thread 클래스를 상속한뒤 run메소드를 오버라이드 한다.

class ThreadEx1 extends Thread{
	public void run(){
     //비즈니스 로직
     }
     
}
public class RunnableThreadTest{
	Thread t = new  ThreadEx1() ;
    t.start
}

3. black box

두번째 방법은 합성을 이용하는 것이다.

class ThreadEx2 implements Runnable {
	public void run(){
     //비즈니스 로직
}
public class RunnableThreadTest2{
	Thread t = new Thread(new RunnableThread());
    t.start();
}

4. 뭐가 더 나은것인가?

상속은 private로 선언되지 않은 모든 변수, 메소드 생성자가 하위 클래스에 노출된다.
이렇게 노출된다는 의미로 white박스라는 의미이다.
상속은 슈퍼클래스가 바뀌면 하위 클래스가 바뀌는 이슈도 생길수 있다.

책에서는 슈퍼클래스가 과일에서 채소로 바뀌는 예시를 들었다.

추가로 기능이 더해질수록, 상속관계가 복잡해져서, 수정과 확장에 손을댈수 없는 상황이 일어날 수 있다.

이처럼 캡슐성과 확장성을 고려하면 , black-box(implements) 이 이롭다.

  • 캡슐화란?
    하나의 클래스를 블랙박스화 하는것이다. 데이터필드는 모두 private로 선언해서 외부접근을 막고, public메소드를 이용해서 내부데이터를 제어, 변경하는것을 캡슐화라고 한다.

하지만 합성은 객체간의 간계가 수평이기 때문에 메소드명이 명확하지 않으면 코드의 가독성이 떨어지기 때문에 클래스들을 패키지로 적절하게 분리하고 인터페이스를 잘 설계해야 한다.

3. 🍤 스레드의 종료

stop()은 여러문제점으로 인하여 썬 마이크로 시스템즈에서 메소드를 사용하지 말것을 권고하고, 있고 두가지 구현방법은 통해 구현할수있다.

1. 플래그를 이용

while(!stoped){

thread.sleep(500);
//지연없이 실행하는것은 cpu에 많은 부담을 준다.

//로직

}
public void stop(){
 stopped = false;
}

public class StopThreadTest{
StopThread st = new StopThread();
Thread thread = new Thread(st);
thread.start();
st.stop();

이처럼 플래그를 이용해 할수 있으나, 특정로직을 무한루프를 돌수있는 문제가 생길수도 있다.

2. interrupt() 메소드 이용

class ThreadEx2 implements Runnable {
	public void run(){
    while(!Thread.currentThread().isInterrupted()){
    
}catch(InterruptedException e){

}finally{
//마무리 해야할것

인터럽트를 이용하여 스레드를 즉각 종료시키고 안전하게 마무리 작업(DB커넥션, 소켓 종료)
을 할 수 있다.

4. 🤳 데몬 스레드와 join

1. 데몬 스레드

자바는 어플리케이션 내부의 모든 스레드가 종료되지 않으면 JVM이 종료되지않는다.
그래서 만약 백그라운드 작업이 일반스레드로 설정되어있다면, 영원히 어플리케이션은 정지되지 않는다.
그래서 자바에서는 데몬스레드라는 개념을 도입했다.

class ThreadEx2 implements Runnable {
	public void run(){
     //비즈니스 로직
}
public class RunnableThreadTest2{
	Thread t = new Thread(new RunnableThread());
    t.setDaemon(true);
    t.start();
    //메인의 실행이 끝나면 쓰레드는 죽는다.
}

2.join

main 스레드가 생성해서 실행시킨 스레드가 종료될까지 main 스레드가 기다려야하는 상황이라면 어떻게 할까?
그럴때 join()이라는 함수를 이용한다.
A , B라는 스레드가 있을때, join을 사용하면 A스레드가 B스레드를 시작시켰을때 B스레드가 끝날때까지 기다린후 자신의 나머지 작업을 계속 진행시킨다.

5. 🤷‍♀️ 스레드의 우선순위

스레드를 여러개 운영하는 어플리케이션이라면 특정 스레드가 먼저 실행되게 하고 싶은 경우가 있을것이다.
A라는 인스턴스에 여러개의 스레드가 접근하려 할때, 한차례에 한개씩 밖에 접근하지못하기 때문이다.

  • 객체와 인스턴스의 차이 : Test reference = new Test();

Thread 클래스에서는 개발자가 스레드의 우선순위를 설정할수있다.

Thread.setPriority() // 1~10까지 안에 숫자를 넣는다. 기본적으로 값이 5 이다.

하지만 스레드 우선순위는 높은수준의 지식없이는 버그가 발생할 가능성이 높기 때문에 가급적 사용하지 말것을 권장한다.

6 . 👀 멀티스레드와 동기화


만약 두개의 스레드가 동시에 같은 값을 바꾸면 어떻게 될까?
1000원의 잔고가 있고, A 쓰레드가 500원을 출금하는 동시에 B쓰레드가 1000원을 출금한다면,
아직 잔고에는 차감되지 않았기때문에 1500원이 출금되어버린다. 그만큼 동기화를 어떻게 해결해야 할까?

1. lock, monitor, synchronized

어떤객체에 여러스레드가 접근할때 동시에 접근하지 못하도록 하기위한것으로, Heap에 lock이 자동생성된다 이렇게 생성된 lock은 sychronized 키워드를 통해 사용한다.

public class SynchronizedTest{

	public synchronized String drawOutString(String money money)
    throws AccountException{
    // 잔액을 계산하여 크면 정상처리하고, 반대일 경우 AccountException을 처리한다.
    }
}
  • 블록이용방법(성능이 저하되기때문에 블록이용방법을 추천!)

public class SynchronizedTest{

	public String drawOutString(String money money)
    throws AccountException{
    
    	synchronized(userAccount){
    	}
    }
}

sychronized를 사용하게 되면

1. 모니터(monitor)가 해당 객체의 락을 검사한다.

2. 쓰레드가 락을 안쓰고 있다면 jvm에게 알려준다.

3. jvm은 'monitorenter'라는 명령으로 락을, 요청한 스레드에게 준다.

4. 블록은 다마치면 락을 반환한다.

2. wait, notify, notifyAll

  • Wait() :락을 반환시키고 waiting상태 돌입(깨워줄때까지 잠수)

  • notify() : waiting 상태인 스레드를 깨우는것

  • notifyall() : waiting을 전부 깨우는것(일반적으로 많이 사용)

3. ThreadLocal

ThreadLocal이란?

일반 변수의 수명은 코드블록 범위내에서만 유효하지만,ThreadLocal은 쓰레드 영역에 변수를 설정할수 있어서 쓰레드가 실행하는 모든영역에서 변수를 사용할 수 있게 된다.
//이후에 계속

좋은 웹페이지 즐겨찾기