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 |