Java Thread 사용방법, Thread에 대해 알아보자 자바 스레드
Java Thread 사용방법, Thread에 대해 알아보자 자바 스레드
Thread에 대해서 알아보기 전에 프로그램과 프로세스의 개념을 잡고 넘어가겠습니다.
프로그램이란 사용자가 실행할 어플리케이션, 즉 하드디스크에 저장된 파일입니다.(실행 전)
프로세스는 이 프로그램을 실행해 메모리에 적재된 인스턴스를 말합니다.(실행 후)
이해가 쉽게 예를 들어보겠습니다.
지금 이 화면을 보고 있는 브라우져도 하드디스크 내 어떠한 경로에 설치되어 있습니다.
브라우저를 실행하기 전엔 이것은 프로그램입니다. 그런데 아이콘을 더블클릭하거나 실행을 하게 되면
프로세스가 되는 것이죠.
그렇다면 쓰레드는 무엇일까요?
쓰레드는 프로세스가 어떠한 작업을 할때 할당 받은 자원(메모리)를 이용하는 작업의 단위를 말합니다.
이제 이 작업의 단위를 구현하는 방법을 알아보겠습니다.
자바에서 쓰레드는 2가지 방법으로 구현할 수 있습니다.
Thread 클래스를 extends 하는 방법. Runable 인터페이스를 implements 하는 방법. 메소드 내 Thread 생성과 동시에 Runnable() 을 하는 방법. |
쓰레드를 사용할 클래스가 다른 클래스를 extends 했다면 다중 extends는 불가하니
Runnable 인터페이스를 implements 해서 사용하고,
extends한 클래스가 없다면 Thread 클래스를 extends 해서 사용합니다.
그럼 각각의 방법을 예시 코드를 보면서 알아보겠습니다.
Thread 클래스를 extends하는 방법
- ThreadTest Class
public class ThreadTest extends Thread{
String name;
public ThreadTest(String name) {
super();
this.name = name;
}
@Override
public void run() {
for(int i=0; i<10; i++) {
System.out.println(getName()+"("+i+") : "+name);
}
}
}
- main method
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
ThreadTest tt = new ThreadTest("A Thread");
tt.start();
ThreadTest tt2 = new ThreadTest("B Thread");
tt2.start();
}
}
ThreadTest Class는 단순히 이름을 10번 출력하는 메소드 입니다. 클래스 코드를 보시면 Thread를 extends 받았고,
그 내에 run() 메소드를 override 하였습니다. run() 메소드는 스레드에서 main() 메소드 기능을 담당하므로 쓰레드에서 실행하고 싶은 작업을 run()메소드에 구현하시면 됩니다. 그리고 getName() 메소드는 Thread 클래스에 정의된 메소드로, 현재 생성된 쓰레드의 이름을 가져옵니다.
main method 부분을 보겠습니다.
Thread 클래스를 인스턴스화 하고(7, 9 line), 그 인스턴의 start() 메소드를 호출(8, 10 line)합니다. 그런데 이상한 점은 우리가 실행되길 원하는 run() 메소드가 아니라 start() 메소드를 호출한다는 점인데요.
이유는 run() 메소드를 호출하면, 새로운 쓰레드가 생성되지 않고, 단순히 override된 메소드 호출이 됩니다. 하지만 start 메소드를 호출하면 새로운 쓰레드가 생성된 후 run() 메소드를 실행하게 되죠.
그러므로 run() 메소드 호출시에는 멀티쓰레딩이 발생하지 않습니다.
위 코드의 결과를 보겠습니다.
쓰레드가 생성되면 내부적으로 앞에 Thread-숫자가 출력되게 됩니다.
하지만 위에서는 Thread-2가 실행이 되다가 Thread-3으로 넘어갔다가 다시 Thread-2가 실행이 것을 볼 수 있습니다.
일반적인 메소드 호출과 달리 쓰레드는 CPU를 차지하는 제어권을 놓고 경쟁하며 차지한 쓰레드가 실행되기 때문입니다.
보다 병렬적인 처리를 위해서 쓰레드 실행을 잠시 멈추는(제어권을 놓는) sleep 메소드를 추가해보겠습니다.
public class ThreadTest extends Thread{
String name;
public ThreadTest(String name) {
super();
this.name = name;
}
@Override
public void run() {
for(int i=0; i<10; i++) {
System.out.println(getName()+"("+i+") : "+name);
try {
sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
실행 결과.
쓰레드가 한번씩 돌아가며 실행되는 것을 볼 수 있습니다.
하지만 어떤 쓰레드가 먼저 실행될지는 개발자가 알 수 없습니다.
Runable 인터페이스를 implements 하는 방법
- ThreadTest2 class
class PrintName{
public void print(String name, int i) {
System.out.println(Thread.currentThread().getName()+"("+i+") : "+name);
}
}
public class ThreadTest2 extends PrintName implements Runnable{
String name;
public ThreadTest2(String name) {
super();
this.name = name;
}
public void run() {
for(int i=0; i<10; i++) {
print(name, i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
- main method
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
ThreadTest2 tt = new ThreadTest2("A Thread");
Thread t1 = new Thread(tt);
t1.start();
ThreadTest2 tt2 = new ThreadTest2("B Thread");
Thread t2 = new Thread(tt2);
t2.start();
}
}
차이점이 보이시나요?
ThreadTest2 클래스는 PrintName 클래스를 extends 하고 있기 때문에 Thread 를 extends 할 수 없습니다. 그래서 Runnable 인터페이스를 implements 해서 구현하고 있죠.
main method 에서 호출 시에도 ThreadTest2 클래스를 인스턴스화 한 후에 Thread 클래스의 매개변수로 넘겨서
그 클래스의 start() 메소드를 실행합니다.
메소드 내 Thread 생성과 동시에 Runnable() 을 하는 방법.
public class ThreadTest3 {
public void start(){
new Thread(new Runnable() {
for(int i=0; i<10; i++) {
System.out.println(i)
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
}
}
쓰레드는 동시에 여러작업을 처리하고(병렬처리) 자원의 효율성을 높이기 위해 사용합니다.
물론 동기화와 같은 주의점이 있긴 하지만 보다 효율적인 로직 처리를 할 수 있습니다.