프로세스
운영체제로부터 메모리를 할당받아 실행중인 프로그램
스레드
하나의 프로세스 내부에서 독립적으로 실행되는 하나의 작업 단위.
- 프로세스에는 주로 한개 이상의 스레드 존재
- main() 이외의 또 다른 스레드를 만들려면 Tread 클래스를 상속하거나 Runnable 인터페이스를 구현해야 한다.
- 스레드는 프로세스의 heap, static, code 영역을 공유한다. (stack을 제외한 메모리 영역 공유)
멀티 프로세스
하나의 응용 프로그램을 여러 개의 프로세스로 구성
멀티 스레드
하나의 응용 프로그램을 여러 스레드로 구성하는 방식.
자바를 실행하면 1개의 메인(main) 스레드에 의해 프로그램이 실행되는데, 하지만 1개의 스레드만으로는 동시 작업을 할 수 없다.
동시에 여러 작업을 처리하기 위해서 별도의 스레드를 더 만드는데 이것을 멀티 스레드라고 한다.
- 장점: 메모리 공유로 인한 시스템 자원소모가 줄어든다. 동시 작업이 가능해진다.
- 단점: 서로 자원을 소모하다가 충돌 가능성, 코딩이 난해해져 버그 생성 확률 높아짐.
💡 멀티 스레드 방식을 사용하는 이유
프로세스는 작업 공간이 독립적이기 때문에 (한 프로세스가 다른 프로세스 변수/자료에 접근할 수 없음), 프로세스끼리 자원 및 데이터를 공유하기 어렵다. 그렇기 때문에 프로세스 간의 데이터 전송이 필요할 경우 시간, 자원 소요가 많다.
하지만, 프로그램 내에 스레드는 서로 독립적이지 않다. 작업공간을 같이 사용하기 때문에 서로 공유가 가능하여 스레드간 데이터 전송은 시간 소요가 적기 때문에 멀티 스레드 방식을 사용한다.
스레드와 프로세스는 동시 작업 처리가 가능한 공통점이 있지만, 스레드는 프로세스보다 오버헤드가 적다는 장점이 있다.
스레드 생성하기
- Tread 클래스 상속받아서 생성
- Runnable 인터페이스 구현하여 생성
Tread 클래스 상속 |
스레드 생성 | java.lang.Thread 클래스를 상속(extends)받아서 run() 메서드 오버라이딩 |
특징 | 실행 스레드로 자신의 콜 스택을 갖춘 독립적인 프로세스 start() 메서드를 통해 스레드가 시작된다 |
|
Runnable 인터페이스 구현 |
스레드 생성 | java.lang.Runnable 인터페이스로부터 run()메서드를 구현하여 생성 (Runnable 인터페이스는 run() 메소드만 가진 함수형 인터페이스) |
특징 | Runnable 인터페이스를 구현한다고 해서 바로 스레드가 되지 않는다. Thread class 를 통해 스레드가 될 수 있다. (방법 : 객체 참조변수를 인자값으로 하는 Thread 생성) |
자바는 단일 상속만 허용하기 때문에, 만약 상속 받아야 하는 클래스가 있다면 Tread 클래스를 상속받는 방법 대신 Runnable 인터페이스를 구현하는 방법을 사용할 수 있다.
일반적으로 Runnable 인터페이스로 생성하는 법을 많이 사용한다.
Tread 클래스 상속받아 스레드 생성
public class 클래스이름 extends Thread{
@Override
public void run() {
// 동시에 실행될 코드 작성
}
}
클래스이름 x = new 클래스이름();
x.start();
Runnable 인터페이스로 스레드 생성
public class 클래스이름 implements Runnable {
@Override
public void run() {
// 동시에 실행될 코드 작성
}
}
클래스이름 x = new 클래스이름();
Tread t = new Tread(x);
t.start();
스레드 예제
1초에 한개씩 별(*)을 10번 출력하고, 동시에 (+)도 한개씩 10번을 출력하는 예제를 작성하자.
1. Tread 클래스 상속
MyTread.java
// 1. Tread 클래스를 상속받는다.
public class MyTread extends Tread {
// *이랑 +만 바뀌니까 필드로
private String str;
public MyTread(String str) {
this.str = str;
}
@Override
public void run() { // 2. run() 메소드 오버라이딩
// 동시에 실행시키고 싶은 코드 작성
for(int i=0; i<10; i++) {
System.out.print(str);
try {
Tread.sleep(1000); // 1초간 쉰다.
}catch(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("end!");
}
}
MyTreadRun.java
public class MyTreadRun {
public static void main(String[] args) {
String name = Tread.currentTread().getName(); // 현재 스레드 이름
System.out.println("스레드 이름: " + name);
System.out.println("start!");
MyTread mt1 = new MyTread("*");
MyTread mt2 = new MyTread("+");
// 3. 스레드는 start()메소드로 실행
mt1.start();
mt2.start();
System.out.println("end!");
}
}
실행결과
스레드 이름: main
start!
end!
*+*+*+*+*+*+*+*+*+*+
코드 설명 (MyTreadRun.java)
1; main() 메소드를 만나면 메인 스레드가 생성돼서, main()을 만나 main을 한줄 한줄 실행한다.
2. 실행하다가 mt1.start() 부분에서 start()를 만나면 mt1에 해당하는 스레드가 발생하면서 run() 메소드가 실행된다.
3. run()이 실행되는 와중에, 메인 스레드는 바로 그 다음줄인 mt2.start()를 또 실행한다.
4. 그러면 또 mt2 스레드가 발생하면서 mt2의 run() 메소드가 실행한다.
5. 그와중에 메인 스레드는 바로 다음줄로 넘어가 end! 출력
모든 스레드가 종료됐을 때, 프로그램이 종료된다.
2. Runnable 인터페이스 구현
Runnable 인터페이스에는 start()가 없고 오직 run() 메소드만 있기 때문에 클래스를 만들때, Tread가 가지도록 해야한다.
스레드를 실행될 준비를 해주는 start()는 Tread 클래스에만 있기 때문이다.
MyRunnable.java
// 1. Runnable 인터페이스 구현
public class MyRunnable implements Runnable {
private String str;
public MyRunnable(String str) {
this.str = str;
}
@Override
public void run() { // 2. run() 메소드 오버라이딩
// 동시에 실행시키고 싶은 코드 작성
String name = Tread.currentThread().getName();
System.out.println("메인 스레드가 아닌 스레드 이름: " + name);
for(int i=0; i<10; i++) {
System.out.print(str);
try {
Tread.sleep(1000); // 1초간 쉰다.
}catch(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("end!");
}
}
MyTreadRun2.java
public class MyTreadRun2 {
public static void main(String[] args) {
String name = Tread.currentTread().getName(); // 현재 스레드 이름
System.out.println("스레드 이름: " + name);
System.out.println("start!");
MyRunnable mr1 = new MyRunnable("*");
MyRunnable mr2 = new MyRunnable("+");
// 3. Tread 인스턴스를 생성하는데, 생성자에 Runnable 인스턴스를 넣어준다.
Tread t1 = new Tread(mr1);
Tread t2 = new Tread(mr2);
// 4. Tread가 가지고 있는 start() 메소드 호출
t1.start();
t2.start();
System.out.println("end!");
}
}
실행결과
스레드 이름: main
start!
end!
메인 스레드가 아닌 스레드 이름: Tread-0
메인 스레드가 아닌 스레드 이름: Tread-1
*+*+*+*+*+*+*+*+*+*+
스레드는 서로 자원을 획득해서 빨리 실행시키고 싶어 해서, 실행 결과가 항상 같지 않다. 어쩔 때는 +가 먼저 나오고, 어쩔 때는 * 가 먼저 출력될 수 있다.
'Java' 카테고리의 다른 글
[Java] static의 의미와 사용법 (0) | 2023.09.01 |
---|---|
[Java] this (0) | 2023.09.01 |
[Java] 인터페이스 (0) | 2023.08.31 |
[Java] final, 불변객체 String (0) | 2023.08.31 |
최상위 객체 Object, 캡슐화 (0) | 2023.08.31 |