티스토리 뷰
우리가 만든 프로그램을 작동시키면 프로세스가 생성되어 CPU로부터 메모리 영역을 할당 받고 명령을 수행한다는 것까지는 이제 알겠다. 하지만, 우리는 네트워크 서비스를 프로그래밍 해야한다. 만약 우리가 만든 네트워크 프로그램에 수천명이 동시에 접속한다고 생각하자. 이 수천명에게 각각 프로세스를 만들어야 한다고 가정해보자. 모든 사람에게 CPU가 메모리 영역을 할당해주어야 한다. 장비의 성능이 좋더라고 운영체제는 무거운 프로세스의 무게를 버티지 못하게 된다. 그래서 새로 나온 개념이 바로 스레드이다.
1. 스레드(Thread)란?
- 프로세스(Process)
- 컴퓨터 메모리에서 연속적으로 실행되는 프로그램 인스턴스
- 각각의 프로세스에 독자적인 메모리 영역 존재
- 프로세스간 전환 속도 느림
- 비정상적인 명령 수행시 해당 프로세스만 종료
- 스레드 (Thread)
- 프로세스 내에서 실행되고 있는 흐름의 단위
- 동일한 프로세스 내에 존재하며, 프로세스의 메모리 영역을 공유
- 스레드간 전환 속도 빠름
- 비정상적인 명령 수행시 동일한 프로세스안의 스레드 모두 종료
2. 스레드의 특징
▶ 개발자의 코드가 복잡해진다.
▶ 데드락(DeadLock) 문제가 발생한다.
▶ 어느 스레드가 먼저 실행될지 모른다.
3. 스레드 실행하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class Solution { public static void main(String[] args) { // TODO Auto-generated method stub Thread t1 = new Thread(); Thread t2 = new Thread(); t1.start(); t2.start(); } } | cs |
위에서는 스레드 t1과 t2를 정의해 실행 시켰다. 스레드를 위에서 선언을 한뒤, start() 메소드로 스레드를 실행 시킨다. 우리가 선언 하는 Thread 클래스를 JVM은 스레드로 인식하여 실행시키게 된다.
하지만 앞서 소개한 스레드는 아무런 행동이 정의 되지 않은 빈 스레드이다. 스레드 행동을 정의하기 위해서는 Thread 클래스를 상속 받아야 한다. 그리고 Thread Class에 존재하는 run() 메소드를 오버라이딩 하여 행동을 정의한다. 그리고 run()메소드에 정의된 프로그램을 실행시키는 메소드가 바로 start()이다.
이를 바탕으로 매우 간단한 스레드 프로그래밍을 만들 수 있다.
TestThread.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class TestThread extends Thread{ int number =0; public TestThread(int a){ this.number = a; } @Override public void run(){ System.out.println("Thread Number : "+number); try { Thread.sleep(5000); // 스레드 잠시 멈춤 } catch (Exception e){ } } } | cs |
MainClass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class Solution { public static void main(String[] args) { // TODO Auto-generated method stub System.out.println("Process start"); for(int i=0; i<10; i++){ TestThread t = new TestThread(i); t.start(); } System.out.println("Process end"); } } | cs |
결과 값
4. 스레드 값 반환하기
앞서 설명한대로 스레드를 그냥 실행 시키면 우리는 어떤 스레드가 먼저 실행될지 전혀 모른다. 물론, 스레드라는 개념 상 우리가 스레드 순서까지 제어해야할 필요는 없다. 하지만, 공용 데이터를 공유한다면 말이 달라진다. 예를들어 해당 스레드를 통해 우리 프로그램의 전역 변수를 제어 해야 하는 경우가 발생 할 수 있다.
이번에는 프로그램에서 전역 변수를 사용하고 변경하는 스레드가 포함된 프로그램을 만들어보자.
A와 B 두사람이 있고, 각각 잔고에는 500원씩 보유하고 있다. 그리고 100원 짜리 제품을 총 5번 구매를 해야하는데 두 사람중 돈이 많은 사람이 구매한다고 하자. 만약, 동일한 잔고를 보유하고 있다면 A가 지불한다. 그렇다면 우리가 원하는 결과값은 A: 200, B: 300이 될 것이다.
스레드 컨트롤에는 여러가지 방법이 있다. 우선 이번에는 간단한 방법을 써보자. 가장 쉽고 직관적인 방법은 스레드가 끝날 때 까지 기다르리는 것이다.
- 스레드 종료 시점까지 대기
MainClass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | public class Solution { static public int a = 500; static public int b = 500; public static void main(String[] args) { // TODO Auto-generated method stub System.out.println("Process start"); for(int i=0; i<5; i++){ TestThread t = new TestThread(a,b); t.start(); try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } a=t.getA(); b=t.getB(); } System.out.println("A : "+a+" B: "+b); System.out.println("Process end"); } } | cs |
TestThread.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | class TestThread extends Thread{ int a =0; int b =0; public TestThread(int a, int b){ this.a = a; this.b = b; } @Override public void run(){ System.out.println("Thread Start"); try { if(a>=b){ a = a-100; } else{ b = b-100; } } catch (Exception e){ } } public int getA(){ return a; } public int getB(){ return b; } } | cs |
결과값
이런식으로 원하는 결과값을 얻을 수는 있겠지만, 매우 불안정한 방법이다. 스레드가 비정상적으로 작동할 수 도 있고 우리가 매번 스레드의 실행 시간을 맞출 수는 없는 노릇이다. 그저 저 시간이면 가능할 것이라고 짐작하고 개발할 뿐이다. 이는 매우 좋지 않은 코딩 방법이다.
이 문제를 어떻게 해결할 수 있을까? 스레드를 스케줄링하는 다양한 방법이 존재하다. 이번 시간에는 스레드의 개념과 java 에서의 스레드 실행 방법에 대해 알아보았다. 다음 시간에는 앞에 발생 한 것 처럼 스레드가 전역 데이터에 접근해야 하거나, 순서 제어 및 동기화를 적용 하는 방법에 대해 배워보도록하자.
'Skill > Programming - Network' 카테고리의 다른 글
7. 스레드 동기화 (0) | 2019.05.19 |
---|---|
6. 스레드 폴링, 콜백 (0) | 2019.05.07 |
4. 프로세스 관리 (Process Control) (1) | 2019.04.07 |
3. 프로세스(Process) (0) | 2019.03.27 |
2. 스트림 (Stream) (0) | 2019.03.03 |