자바 프로그램에서의 쓰레드
자바로 작성한 프로그램을 실행하면 메모리로 로딩돼 프로세스 상태가 된다.
.class 파일을 실행하면 자바 가상 머신은 main 쓰레드를 생성한다.
즉, 프로그램이 처음 실행되면 시작 지점에서 main쓰레드 1개만이 존재한다.
main()메서드에서 작성한 내용이 바로 이 main쓰레드에서 동작한다.
mian쓰레드의 내부에서 2개 이상의 쓰레드를 생성해 실행하면 동시에 N개 이상의 쓰레드가 동작하게 되는데, 이를 멀티 쓰레드라고 한다.
멀티 쓰레드의 필요성
만약 FPS게임을 하고 있다고 가정하자
총을 연발로 5발을 쐈을 때 쏘는 화면과 발사 소리를 출력한 것을 나타낸 것이다.
public class TheNeedForThread {
public static void main(String[] args) {
int[] intArray = {1,2,3,4,5};
String[] stringArray = {"일","이","삼","사","오"};
for(int i =0; i<intArray.length;i++){
System.out.println("(총쏘는 프레임)"+intArray[i]);
try{
Thread.sleep(200);
} catch (InterruptedException e) {}
}
for(int i =0; i<stringArray.length;i++){
System.out.println("(사운드 번호)"+stringArray[i]);
try{
Thread.sleep(200);
} catch (InterruptedException e) {}
}
}
}
우리가 예상한 결과는
총쏘는 장면1 > 총쏘는 소리1 > 총쏘는 장면2> 총쏘는 소리2 > ... 순으로 예상했지만
결과는 총쏘는 장면1> 총쏘는 장면2 > 총쏘는 장면3..4..5 > 총쏘는 소리1..2..3..4..5 순으로 출력된다.
이는 우리가 원하는 결과가 아니다.
이 문제를 해결 할 수 있는 방법은 멀티 쓰레드를 사용하는 것이다.
멀티 쓰레드가 아닌 싱글 스레드를 사용했을 때 각 작업은 순차적으로 처리된다.
즉 먼저 시작된 작업이 완전히 종료되면 두번째 작업이 실행된다.
멀티 쓰레드는 동시성(Concurrenct)과 병렬성(Parallelism)을 갖고 수행한다.
동시성
동시성은 처리할 작업수가 CPU 코어 수 보다 많을 때이다. CPU는 각 작업 쓰레드의 요청 작업을 번갈아 수행하게 되는데 매우 짧은 간격으로 교차 실행하여 사용자 입장에서는 마치 동시에 실행하는 것처럼 보인다.
병렬성
병렬성은 CPU코어수가 작업 수 보다 많을 때이다. 각 작업은 각각의 코어에 할당해 동시에 실행할 수 있기 때문에 동시에 작업이 수행된다.
쓰레드의 생성 및 실행
쓰레드를 생성하는 방법은 두가지 방법이 있다.
첫번째 방법은 Thread클래스를 상속받아 run()메서드를 오버라이딩 하는 것 이다.
두번째 방법은 두 단계로 이루어져 동작한다.
첫번째 과정에서 Runnable인터페이스를 구현한 Runnable객체를 생성한다. 이 인터페이스는 추상메서드로 run()메서드를 갖고 있다. 인터페이스 객체 생성 과정에서 run()메서드를 구현해야 한다.
두번째 과정에서 Thread객체를 생성할 때 첫번째 과정에서 생성한 runnable객체를 생성자로 전달하는 것이다.
2가지 생성방법 모두 run()메서드를 재정의하고, Thread객체를 생성한다.
생성한 Thread객체 내의 start()메서드를 호출하여 run()메서드를 실행시킨다.
run() 메서드와 start()메서드의 차이점?
실제 CPU와 통신하기 위해서는 스택메모리를 포함해 준비해야 할것들이 있다. start()메서드는 '새로운 쓰레드 생성/추가를 위한 모든 준비' , '새로운 쓰레드 위에어 run()실행' 이라는 2가지 작업을 연속으로 한다.
start() = 새로운 쓰레드 생성/추가하기 위한 모든 준비 + 새로운 쓰레드 위에 run()실행
1. Thread 클래스를 상속받아 run() 메서드 재정의
실행 과정은 다음과 같다.
// 1.클래스 정의 (Thread클래스를 상속받아 run()메서드를 오버라이딩한 클래스 정의
class MyThread extends Thread{
@Override
public void run(){
// 쓰레드 실행
}
}
// 2.객체 생성
Thread myThread = new MyThread();
OR
MyThread myThread = new myThread();
// 3. 쓰레드 실행
myThread.start();
2개 쓰레드 활용 예제
//Thread클래스를 상속해 클래스를 생성한 후 쓰레드 2개 생성
class FileThread extends Thread{
@Override
public void run(){
// 총쏘는 장면
int[] intArray = {1,2,3,4,5};
for(int i =0; i<intArray.length;i++){
System.out.println("(총쏘는 프레임)"+intArray[i]);
try{ Thread.sleep(200); } catch (InterruptedException e) {}
}
}
}
public class MultiThreadTest {
public static void main(String[] args) {
//FileThread 객체 생성 및 시작
Thread fileThread = new FileThread();
fileThread.start();
// 총쏘는 사운드
String[] stringArray = {"일","이","삼","사","오"};
try{ Thread.sleep(200); } catch (InterruptedException e) {}
for(int i =0; i<stringArray.length;i++){
System.out.println("(사운드 번호)"+stringArray[i]);
try{ Thread.sleep(200); } catch (InterruptedException e) {}
}
}
}
결과
(총쏘는 프레임)1
(사운드 번호)일
(총쏘는 프레임)2
(사운드 번호)이
(총쏘는 프레임)3
(사운드 번호)삼
(총쏘는 프레임)4
(사운드 번호)사
(총쏘는 프레임)5
(사운드 번호)오
3개 쓰레드 활용 예제
총쏘는 장면 쓰레드와 총쏘는 소리 쓰레드, main(2개의 쓰레드 객체를 생성해 실행) 쓰레드 총 3개 쓰레드를 사용해 수행할 수 있다.
class FrameThread extends Thread{
@Override
public void run(){
// 총쏘는 장면
int[] intArray = {1,2,3,4,5};
for(int i =0; i<intArray.length;i++){
System.out.println("(총쏘는 프레임)"+intArray[i]);
try{ Thread.sleep(200); } catch (InterruptedException e) {}
}
}
}
class SoundThread extends Thread{
@Override
public void run(){
// 총쏘는 장면
String[] stringArray = {"일","이","삼","사","오"};
try{ Thread.sleep(200); } catch (InterruptedException e) {}
for(int i =0; i<stringArray.length;i++){
System.out.println("(사운드 번호)"+stringArray[i]);
try{ Thread.sleep(200); } catch (InterruptedException e) {}
}
}
}
public class MultiThreadTest2 {
public static void main(String[] args) {
//FrameThread 객체 생성 및 시작
Thread frameThread = new FrameThread();
frameThread.start();
//SoundThread 객체 생성 및 시작
Thread soundThread = new SoundThread();
soundThread.start();
}
}
위의 결과와 같다.
2. Runnable 인터페이스 구현 객체를 생성한 후 Thread 생성자로 Runnable 객체 전달
1. 클래스 정의 - Runnable 인터페이스를 구현한 클래스 정의 (추상 메서드 run()구현)
class MyRunnable implements Runnable {
@Override
public void run() {
//쓰레드 작업 내용
}
}
2. Runnable 객체 생성, Thread 객체 생성(생성자에 Runnable 객체 전달)
Runnable r = new MyRunnable();
OR
MyRunnable r = new MyRunnable();
Thread myThread = new Thread(r);
3. 쓰레드 실행 - start() 메서드를 이용해 쓰레드 실행
myThread.start();
(Runnable 인터페이스를 구현한 클래스를 직접 정의하는대신 익명 이너 클래스 정의 방법을 이용해 바로 객체를 생성할 수 도 있다.)
2개 쓰레드 활용 예제
class FrameRunnable implements Runnable{
@Override
public void run(){
// 총쏘는 장면
int[] intArray = {1,2,3,4,5};
for(int i =0; i<intArray.length;i++){
System.out.println("(총쏘는 프레임)"+intArray[i]);
try{ Thread.sleep(200); } catch (InterruptedException e) {}
}
}
}
public class MultiThreadTest3 {
public static void main(String[] args) {
//FileThread 객체 생성 및 시작
Runnable frameRunnable = new FrameRunnable();
Thread thread = new Thread(frameRunnable);
thread.start();
// 총쏘는 사운드
String[] stringArray = {"일","이","삼","사","오"};
try{ Thread.sleep(200); } catch (InterruptedException e) {}
for(int i =0; i<stringArray.length;i++){
System.out.println("(사운드 번호)"+stringArray[i]);
try{ Thread.sleep(200); } catch (InterruptedException e) {}
}
}
}
결과
(총쏘는 프레임)1
(사운드 번호)일
(총쏘는 프레임)2
(사운드 번호)이
(총쏘는 프레임)3
(사운드 번호)삼
(총쏘는 프레임)4
(사운드 번호)사
(총쏘는 프레임)5
(사운드 번호)오
3개 쓰레드 활용 예제
방법 1과 마찬가지로 쓰레드 3개를 이용해서 수행 할 수 있다.
class FrameRunnable implements Runnable {
@Override
public void run(){
// 총쏘는 장면
int[] intArray = {1,2,3,4,5};
for(int i =0; i<intArray.length;i++){
System.out.println("(총쏘는 프레임)"+intArray[i]);
try{ Thread.sleep(200); } catch (InterruptedException e) {}
}
}
}
class SoundRunnable implements Runnable{
@Override
public void run(){
// 총쏘는 사운드
String[] stringArray = {"일","이","삼","사","오"};
try{ Thread.sleep(200); } catch (InterruptedException e) {}
for(int i =0; i<stringArray.length;i++){
System.out.println("(사운드 번호)"+stringArray[i]);
try{ Thread.sleep(200); } catch (InterruptedException e) {}
}
}
}
public class MultiThreadTest4 {
public static void main(String[] args) {
//FrameThread 객체 생성
Runnable frameRunnable = new FrameRunnable();
// frameRunnable.start() // Runnable객체에는 start()메서드가 없어 오류 발생
Thread thread1 = new Thread(frameRunnable);
thread1.start();
//SoundRunnable 객체 생성
Runnable soundRunnable = new SoundRunnable();
Thread thread2 = new Thread(soundRunnable);
thread2.start();
}
}
이너 클래스를 활용한 쓰레드 객체 생성 및 실행
이 예제는 3개의 쓰레드가 동작하는 앞의 예제와 동일하지만 별도의 클래스를 정의하지 않고, 익명 클래스의 문법을 이용해 Runnable인터페이스 객체를 생성한 이후에 실행한다는 점에서 차이가 있다.
main 쓰레드와 2개의 이름이 없는 익명 클래스가 동작한다.
public class MultiThreadTest5 {
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run(){
// 총쏘는 장면
int[] intArray = {1,2,3,4,5};
for(int i =0; i<intArray.length;i++){
System.out.println("(총쏘는 프레임)"+intArray[i]);
try{ Thread.sleep(200); } catch (InterruptedException e) {}
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run(){
// 총쏘는 사운드
String[] stringArray = {"일","이","삼","사","오"};
try{ Thread.sleep(200); } catch (InterruptedException e) {}
for(int i =0; i<stringArray.length;i++){
System.out.println("(사운드 번호)"+stringArray[i]);
try{ Thread.sleep(200); } catch (InterruptedException e) {}
}
}
});
thread1.start();
thread2.start();
}
}
'Language > Java' 카테고리의 다른 글
[Java] 쓰레드의 동기화 (0) | 2022.04.06 |
---|---|
[Java] 쓰레드의 속성 (0) | 2022.04.05 |
[Java] 예외 전가 (throws) (0) | 2022.04.05 |
[Java] 리소스 자동 해제 예외 처리 (0) | 2022.04.04 |
[Java] 예외 처리 (0) | 2022.04.04 |