Practice makes perfect
[JAVA] Thread 본문
Thread 란?
사용자가 작성한 코드로서, JVM에 의해 스케줄링되어 실행되는 단위입니다.
쉽게 설명해서 하나의 프로세스 안에서 2가지 이상의 일을 수행하는 것과 같은 것과 같은 효과를 주는 것을 의미합니다.
Java Thread
- 자바 가상 기계(JVM)에 의해 스케쥴되는 실행 단위의 코드 블럭입니다.
- 스레드의 생명 주기는 JVM에 의해 관리됩니다.
JVM과 멀티스레드의 관계
- Thread 는 운영체제와 관련있는 기능으로 java 이외에 thread를 구현해주는 프로그램이 없습니다.
- 하나의 JVM은 하나의 자바 응용프로그램만 실행합니다.
- 자바 응용프로그램이 시작될 때 JVM이 함께 실행합니다.
- 자바 응용프로그램이 종료하면 JVM도 함께 종료합니다.
- 하나의 응용프로그램은 하나 이상의 스레드로 구성 가성합니다.
-- Thread 클래스 작성(Thread 클래스 상속. 새 클래스 작성)
class TimerThread extends Thread {
-- Thread 코드 작성
run() 메소드 오버라이딩
@Override
public void run() { // run() 오버라이딩
-- Thread 객체 생성
TimerThread th = new TimerThread();
-- Thread 시작(start() 메소드 호출)
th.start();
run() 메소드 오버라이딩
- run() 메소드를 Thread 코드라고 부릅니다.
- run() 메소드에서 Thread 실행 시작합니다.
- run(); 을 통해서 main 2개인것과 같은 효과를 가지게 해주며, main과 동시 수행합니다.
start() 메소드 호출
- Thread로 작동 시작하고 JVM에 의해 스케줄되기 시작합니다.
- start(); 호출하러 가자마자 바로 복귀를 시켜서 다음 명령어를 수행하고, 운영체제에게 명령수행하기 전 준비하도록 요청합니다.
- Thread 수행하기 전 메모리들을 운영체제에게 요청해서 thead의 메모리를 할당받아 스레드의 run을 호출하게끔 구현이 되어져있습니다. main은 main 대로 수행하고, start로 바로 복귀 시켜 두개가 함께 수행하도록 합니다.
class SumThead extends Thread{
String threadName;
int start, end;
SumThead(String threadName, int start, int end){
this.threadName = threadName;
this.start = start;
this.end = end;
}
@Override
public void run() {
int sum = 0;
for(int i = start ; i <= end; i++) {
sum+=i;
System.out.println(threadName);
}
System.out.printf("%s => %d ~ %d 까지의 합 : %d \n", threadName, start, end, sum);
}
}
public class ThreadUnderstand {
public static void main(String[] args) {
SumThead thread1 = new SumThead ("쓰레드1", 1, 10); // main 안에서 new 하는 컨셉은 변함없다.
// 1~10까지의 합
SumThead thread2 = new SumThead ("쓰레드2", 11, 20);
// 11~20까지의 합
int sum = 0;
thread1.start();
thread2.start();
for(int i = 1 ; i <= 50 ; i++) {
sum += i;
System.out.println("main");
}
System.out.println("main() 메서드 실행 => 1~50까지의 합 : " + sum);
System.out.println("프로그램 종료.");
}
}
결과값 : main, thread1, thread2 함께 수행하는 효과를 주지만 출력되는 순서는 일정하게 나타나지 않습니다. 이것은
현재 운영되는 프로그램에 따라서 다른 결과를 나타나게 됩니다. 결과의 순서는 운영체제의 알고리즘만 알 수 있습니다.
결과값을 통해서 3개의 작업이 골고루 수행되는 효과를 보여줄 수 있습니다.
자료형을 선언했을 때, 그 자료형을 thread 로 상속받고 싶을 때 다른 class 를 상속받았을 때 사용법)
=Runnable 사용
class Sum {
int num;
Sum(){num=0;}
public void addNum(int num) {this. num += num;}
public int getNum() {return num;}
}
class AddThread extends Sum implements Runnable{
AddThread(int start , int end){
this.start = start;
this.end = end;
}
int start, end;
@Override
public void run() {
for(int i = start; i <= end ; i++){
addNum(i);
}System.out.printf("%d ~ %d 의 총 합은 : %d \n", start, end, num);
}
}
public class RunnalbleThread {
public static void main(String[] args) {
AddThread at1 = new AddThread(0,50); // thread 기능이 탑재 되어 있지 않다.
// runnable을 상속받는 AddThread을 사용하여 run을 사용한다.
AddThread at2 = new AddThread(51,100);
Thread thread1 = new Thread(at1); // run 을 수행시킬 코드를 만들어줘야 한다.
Thread thread2 = new Thread(at2);
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// join() method는 try~each 문으로 감싸줘야하며,
// join() method는 run(); method가 수행될때까지 기다린다.
System.out.println("0~100까지의 합 : " + (at1.getNum()+at2.getNum()));
※ thread기능을 탑재 시키기 위한 유일한 방법
- Sum 을 상속 받아서 thread를 상속하지 못할 때 implements Runnable 인터페이스 사용합니다.
- Runnable 안에 run(); 추상method 하나 들어가있습니다. 또한 thread와는 상관이 없으며 단지 run method 만
오버라이딩 되어 있는 것입니다.
- join() method는 run(); method가 수행될때까지 기다렸다가 수행합니다.
여기서 위의 코드의 문제점!
(at1.getNum()+at2.getNum())의 값이 계속 다르게 나타납니다.
그 이유는 thread1, thread2 , main 함께 시작하기 때문에 컴퓨터의 상태에 따라서 언제 실행될지 알 수 없습니다.
thread1, thread2이 구동되기 전에 main은 벌써
System.out.println("0~100까지의 합 : " + (at1.getNum()+at2.getNum())); 실행할수도 때문에 결과가 항상 다르게 출력합니다.
thread1, thread2가 구동이 되고 나서 System.out.println("0~100까지의 합 : " + (at1.getNum()+at2.getNum())); 실행 되어야지 올바른 결과값이 나타납니다.
위의 코드를 해결 우선순위를 넣어서 코드를 만들어보겠습니다.
우선순위)
class MessageSendingThread extends Thread{
String message;
// 우선순위 부여(절대적인 것이 아닌 가중치정도록 생각하자.)
MessageSendingThread(String message, int prio){
this.message = message;
setPriority(prio); // 값을 다시 setting 할 수 있게 만들어준다.
}
@Override
public void run() { // main 2개 효과
for(int i = 0; i < 1000; i++) {
System.out.printf("%s(%d) \n" , message, getPriority());
}
}
}
public class PriorityTest {
public static void main(String[] args) {
// 우선순위
MessageSendingThread tr1 = new MessageSendingThread ("First", Thread.MAX_PRIORITY);
// ("First", 10);
MessageSendingThread tr2 = new MessageSendingThread ("Second", Thread.NORM_PRIORITY);
// ("Second", 5);
MessageSendingThread tr3 = new MessageSendingThread ("Third", Thread.MIN_PRIORITY);
// ("Third", 1);
tr1.start();
tr2.start();
tr3.start();
}
}
- setPriority(prio) 을 생성자에 초기화 시켜줍으로 run(); method안에 숫자를 넣어 우선순위 대로 출력하도록 만들어줍니다.
- 우선순위가 부여되었다고 해서 Thread의 특성상 결과에 있어서 정확하게 순서에 맞춰서 출력되지는 않습니다. 각각 1000가지 씩 수행되어야 하는데, 우선순위가 낮다고 해서 무조건 늦게 나오는 것이 아니라는 점을 염두해두면 좋겠습니다.
동기화)
: 하나의 thread 의 동작이 완전히 진행된 이후 순서를 넘기는 것을 의미합니다. (synchronized)
class Sum{
int num;
Sum(){num=0;}
public synchronized void addNum(int num) {this. num += num;}
public int getNum() {return num;}
}
class AdderThread extends Thread{
Sum sumInst ;
int start, end;
AdderThread(Sum sum, int s , int e){
this.sumInst = sum;
this.start = s;
this.end = e;
}
@Override
public void run() {
for(int i =start ; i<= end ; i++) {
sumInst.addNum(i);
}
}
}
public class ThreadHeapMultiAccess {
public static void main(String[] args) {
Sum sum = new Sum();
AdderThread at1 = new AdderThread(sum , 1 , 5000); // sum의 주소값이 저장
AdderThread at2 = new AdderThread(sum , 5001 , 10000); // sum의 주소값이 저장
at1.start();
at2.start();
try {
at1.join();
at2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
최종적으로 업데이트가 균일하게 진행되면 문제가 없지만 그렇지 못하게 때문에 나오는 문제점입니다.
위의 코드를 보면 at1 과 at2 가 같은 data를 가지고 수행하는데 Thread의 특성으로 인해서 올바른 값이 출력되지 않습니다. 이러한 문제점을 해결하기 위해서 동기화를 하나의 값이 정확히 수행되고 나서 다음 값이 진행되록 만들수 있습니다.
- 위와 같이 둘 이상의 thread 가 heap 영역하나의 data 를 바라보도록 접근해서 처리하게 되면 문제가 생기므로 동시화를 고려해야 합니다. 왜냐하면 순서가 왔다갔다 하면서 수행을 하게 되면 결과 값이 날아가버리기 때문입니다.
- synchronized 를 활용하여 동작이 완료되서 복귀 될 때까지 순서가 넘어가지 않도록 할 수 있습니다. 단, 제어권을 넘기지 않고 다 수행한 이후 제어권을 넘기도록 하면 출력 속도가 느려지기 때문에 꼭 필요한 경우에 사용하는 것을 권장합니다.
'빅데이터 > JAVA' 카테고리의 다른 글
[JAVA] 네트워크(NETWORK) (0) | 2020.05.23 |
---|---|
[JAVA] 스트림(Stream) (0) | 2020.05.20 |
[JAVA] 컬렉션 프레임워크 ( Collection Framework ) (0) | 2020.05.18 |
[JAVA] Generics (0) | 2020.05.17 |
[JAVA] Calendar 클래스, Date 클래스 , Random 클래스 (0) | 2020.05.17 |