<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
출처
반응형
'공부(2018~2019) - 스킨변경전 > Socket' 카테고리의 다른 글
<Socket> 5. WebSocket 정리 (2) | 2019.05.21 |
---|---|
<Socket> 4. Socket Programming 정리 (0) | 2019.05.14 |
<Socket> 3. Network Programming 정리 (0) | 2019.05.08 |
<Socket> 2. I/O Stream 정리 (0) | 2019.05.07 |
블로그의 정보
57개월 BackEnd
BFine