You will be fine

<Socket> 1. Thread 정리

by BFine
반응형

Thread 

공부하기 전에…

Socket 프로그래밍을 막상 다시 해보려니까 이를 위한 기본적인 부분들이 머리 속에 정리가 되지 않은 것 같아서 겸사겸사 정리해야겠다는 생각이 들었다.

 

 

1.1 프로세스와 다중 프로세스

  • 멀티프로그래밍은 운영체제의 프로세스 관리기법 중 하나이다.
  • 프로그램이란 명령어들의 집합이고 실행되려면 주기억장치(메인메모리)에 로드되어야한다.
  • 주기억장치는 RAM과 ROM으로 구성되어있다.
  • 초기에는 하나의 프로세스만이 로드되어 CPU를 독점했기 때문에 입출력이 끌날때까지 CPU의 유휴시간이 발생했다.
  • 멀티프로그래밍이란 여러 프로세스를 메모리에 로드하고 운영체제가 CPU의 사용권을 다른프로세스로 넘겨 CPU를 최대한 사용하는 관리 기법 이다.

 

1.2 스레드란

  • 하나의 프로세스는 cpu시간, 메모리, 파일, 입출력 장치등 상당한 양의 상태정보를 관리해야한다.
  • 프로세스간 스위칭을 컨텍스트 스위칭 이라 한다. (프로세스 영역에 변화)
  • 이 스위칭이 발생하면 대량의 상태정보를 저장&관리 해야되는데 프로세스에서는 부담스러운 작업이다.
  • 이 문제점을 해결하는 것이 경량화 된 프로세스 다른 말로 스레드 라고 부른다.
  • 프로세스를 나라, 스레드를 지역으로 본다면 스위치을 세분화 하는 것과 같다.
  • 스레드는 프로세스 내에서 하나의 작업단위 (실행 가능한 최소단위)이다.
  • 실행에 필요한 최소한의 데이터만을 가지고 다른 스레드들과 프로세스 실행환경을 공유한다.
  • 힙, 클래스변수, 메서드영역 은 공유하고 레지스터, 스택영역은 각각 갖는다.(멀티스레드)

 

1.3 스레드활용

  • 메인스레드 는 프로세스가 생성되면 별도의 요청없이 하나의 스레드가 생성되며 이를 기반으로 실행된다.(자동생성)
  • 스레드는 제어 흐름을 순차적에서 동시로 바꿔 CPU의 유휴시간을 최소화한다. (응답시간 단축)

 

2.1 스레드의 생성

  • 스레드는 하나의 독립적인 작업을 실행하는 코드와 개별 데이터 를 가지고 있어야한다.
  • 자바에서 코드와 개별 데이터를 갖는 독립적인 단위는 객체 이다.
  • 객체는 코드(메서드)와 데이터(인스턴스변수) 를 갖는다.
  • Thread 클래스의 run()은 스레드의 생명주기를 관리하며 cpu 사용권이 주어지면 호출 되도록 약속된 메서드다.
  • 대기장소인 pool에 있는 스레드 중 하나의 스레드가 dispatch(선택) 되어 실행상태가 되면 run()을 호출한다.

 

2.2 스레드의 구현

  • 이미 실행된 스레드에 대해 재가동 할 수 없다.
생성자 설명
Thread(String name) 스레드의 이름을 설정하여 생성한다.
Thread(Runnable r, String name) 스레드의 이름을 설정하여 생성하고 Runnable 활용하여 생성한다.

 

2.3 스레드의 생명주기

  • 스레드는 new 상태, ready 상태, running상태 , blocked 상태(일시적 중단 or 대기), exit 상태를 가진다.
  • start()가 실행되었다고 해서 바로 스레드가 실행되는 것이 아니고 blocked 상태로 있다가 스케줄러에 의해 순서가 결정된다.

 

2.4 스레드 제어

메서드 설명
yield() 실행중인 스레드를 일시중단한다.
sleep() 실행중인 스레드를 지정시간동안 수면상태로 만들고 시간이 지나면 준비상태가 된다.
interrput() 스레드를 가로챈다.
join() 실행중인 스레드에 지정시간동안 종료되지않고 대기상태가 되고 시간이 지나면 준비상태가 된다.

 

2.4.1 sleep()

  • 이 메서드가 호출되면 슬립풀로 들어가며 일시중지 상태 가 된다.(ms단위)
  • 시간이 경과하거나 interrput()에 의해 슬립풀에서 빠져나와 실행대기상태 풀로 들어가 대기상태가 된다.
  • sleep()는 다른 스레드에게 cpu 사용권을 넘겨주기 위한 목적 으로 사용된다.
Thread thread1 = new Thread(()-> IntStream.rangeClosed(1,5).forEach(i->{
            try {
                System.out.println(i+"-thread1");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }) ,"a");
        Thread thread2 = new Thread(()-> IntStream.rangeClosed(1,5).forEach(System.out::println) ,"b");
 
thread1.start();
thread2.start();
 
********** 실행 결과 *************
1-thread1
1
2
3
4
5
2-thread1
3-thread1
4-thread1
5-thread1

 

2.4.2 join()

  • 이 메서드가 호출되면 조인 풀로 들어가 대기상태가 된다.
  • 이 메서드에게 메세지를 받은 스레드가 모두 종료 되야 실행대기상태 풀로 들어간다.
  • 시간을 지정하면 종료되거나 시간이 끝나면 상태가 전환된다.(+ interrput)
  • 스레드간 작업이 시간에 따라 영향받거나 선후관계가 있으면서 다른 스레드와 협업 할때 사용한다.
  • 스레드의 상태를 알고싶을 경우는 ThreadState를 사용한다.
  • 스레드를 중지시킬때는 interrput()를 사용한다.
Thread thread1 = new Thread(()-> IntStream.rangeClosed(1,5).forEach(i->{
               System.out.println(i+"-thread1");
       }) ,"a");
       Thread thread2 = new Thread(()-> IntStream.rangeClosed(1,5).forEach(i->{
               System.out.println(i+"-thread2");
           try {
               thread1.join();
               // join을 호출한 스레드 thread2는 thread1이 끝날때까지 대기상태가 된다.
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }) ,"b");
       thread1.start();
       thread2.start();
       System.out.println("main");
 
thread1.start();
thread2.start();
 
********** 실행 결과 *************
main // 메인스레드는 영향받지 않는다.
1-thread2
1-thread1
2-thread1
3-thread1
4-thread1
5-thread1
2-thread2
3-thread2
4-thread2
5-thread2

 

3.0 스레드의 우선순위

  • setPriority()를 이용해 스레드에 우선순위 를 부여한다. 매개변수 숫자가 높을수록 높은 우선순위를 가진다.
  • sleep 메소드는 우선순위를 일시적으로 지연시킬 수 있다.

 

4.0 스레드의 그룹

  • 스레드들을 그룹으로 묶어 한꺼번에 제어하는 그룹관리 기술을 제공 한다.
  • 모든 스레드는 반드시 하나의 그룹에 속해야한다. 지정하지 않으면 메인스레드 그룹에 속한다.
  • 자신을 생성한 스레드 그룹으로 부터 우선순위를 상속받는다.
  • ThreadGroup 클래스를 사용하여 구현한다.
public static void main(String[] args) throws InterruptedException {
    ThreadGroup apple = new ThreadGroup("apple");
    ThreadGroup banana = new ThreadGroup("banana");
    MyThread t1 = new MyThread(apple,"Thread1");
    MyThread t2 = new MyThread(apple,"Thread2");
    MyThread t3 = new MyThread(banana,"Thread3");
    MyThread t4 = new MyThread(banana,"Thread4");
    t1.start();
    t2.start();
    t3.start();
    t4.start();
    apple.interrupt();
}
 
static class MyThread extends Thread{
    ThreadGroup group;
    public MyThread(ThreadGroup group, String name) {
        super(group, name);
        this.group = group;
    }
    @Override
    public void run() {
            try {
                for(int i = 1; i <= 3; i++) {
                    System.out.println(getName() + " : " + i + " " + group.getName());
                    sleep(1000);
                }
            } catch (InterruptedException e) {
                System.out.println("인터럽트 발생 : "+getName()+" 종료");
            }
        }
    }
 
********** 실행 결과 *************
Thread1 : 1 apple // sleep 제어권 넘어감
Thread2 : 1 apple
Thread4 : 1 banana
Thread3 : 1 banana // main 스레드로 제어권 넘어감
// apple 그룹의 interrput 호출
인터럽트 발생 : Thread2 종료 // catch로 넘어감
인터럽트 발생 : Thread1 종료
Thread3 : 2 banana
Thread4 : 2 banana
Thread4 : 3 banana
Thread3 : 3 banana

 

5.0 스레드의 동기화

  • 기본적으로 스레드는 독립적인 실행단위 로 서로의 작업에 영향을 주지 않게 구현한다.
  • 멀티스레드 환경에선 공유자원을 이용해 작업을 수행하는 경우가 있다.
  • 자원을 공유하는 영역 (임계영역)이 있다면 작업이 종료될때까지 다른 스레드에게 뺏기지 않는(비선점) 것이 필요하다.
  • 이를 동기화 라하고 공유된 자원에 접근할때 한번에 하나의 스레드만 접근할 수 있도록 잠금을 걸어 데이터의 일관성 을 유지하는 것이다.
  • 스레드 동기화는 두가지가 있는데 실행순서, 메모리접근 방식이있다.

 

5.1 synchronized 동기화

  • 객체에 잠금하는 방법과 메서드에 잠금하는 두가지 방법이 있다.

 

5.2 동기화로 인한 교착 상태

  • 두스레드가 잠금을 한상태에서 서로 잠금이 풀리기를 기다리는 상황을 말한다.
static Integer num1 = 1000;
    static Integer num2 = 500;
    static Object o1 = new Object();
    static Object o2 = new Object();
    public static void main(String[] args) throws InterruptedException {
 
        Thread t1 = new Thread(()->{
           synchronized (o1){
               try {
                   num1 -= 500;
                   Thread.sleep(2000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               synchronized (o2){
                   num2 -= 100;
                   System.out.println(num2);
               }
           }
        });
        Thread t2 = new Thread(()->{
           synchronized (o2){
               try {
                   num2 -= 500;
                   Thread.sleep(2000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               synchronized (o1){
                   num1+=100;
                   System.out.println(num1);
               }
           }
        });
        t1.start();
        t2.start();
}
********** 실행 결과 *************
// 아무것도 출력되지 않는다. process는 데드락상태
// 서로 자뭔을 잠그고 다른 잠긴 자원을 원하기 떄문이다. 소유권이 넘어가도 작업할 수 없다.

 

5.3 스레드 간 조정

  • 스레드간 조정은 wait()notify()를 이용해 구현한다.이 둘은 Thread가 아닌 Object에 속해있다.
  • 모든 객체는 대기풀에 있고 객체의 wait()을 실행하고 실행을 멈춘 스레드들이 여기에 저장된다.
  • wait() 호출되면 현재 실행중인 스레드는 전달받은 객체의 대기풀로 들어가 대기상태가 된다.
  • 대기풀에 여러 스레드가 있을때 notify()가 실행되면 이중 하나의 스레드만 준비상태 풀로 들어간다.(선입선출)
  • 해당 객체의 잠금 영역 안에서만 사용 이 가능하다.
Thread t1 = new Thread(()->{
   synchronized (o1){
       try {
           System.out.println("Thread1 실행");
           System.out.println("Thread1 소유권 전환");
           Thread.sleep(2000);
           System.out.println("Thread1 복귀");
           System.out.println("Thread1 대기");
           o1.wait(); // o1잠금해제
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       synchronized (o2){
           System.out.println("Thread1 실행2");
       }
   }
});
Thread t2 = new Thread(()->{
    synchronized (o2){
        try {
            System.out.println("Thread2 실행");
            System.out.println("Thread2 소유권 전환");
            Thread.sleep(2000);
            System.out.println("Thread2 복귀");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized (o1){
            System.out.println("Thread2 실행2");
            o1.notify();
        }
    }
});
t1.start();
t2.start();
 
********** 실행 결과 *************
Thread1 실행
Thread1 소유권 전환
Thread2 실행
Thread2 소유권 전환
Thread1 복귀
Thread1 대기 // wait
Thread2 복귀
Thread2 실행2 // notify 호출
Thread1 실행2

 

출처

 

반응형

블로그의 정보

57개월 BackEnd

BFine

활동하기