선수지식
- 자바의 객체란 무엇인가?
- 프로그램 상에서 구현할 대상
- 클래스에 선언된 정보 그대로 생성된 실체이다.
- enum은 무엇인가?
- 관련이 있는 상수들의 집합
- Java의 Object클래스는 어떤 클래스이고, 어떤 메소드가 있는가?
- .equals()
- .hashCode()
- .toString()
- 생성자에서 사용하느 super()가 어떤 용도로 사용되는지?
- 자식클래스에서 부모클래스의 생성자를 호출하는 메소드
- 자식클래스라 할지라도 부모클래스의 priavate한 변수와 메소드에 접근이 불가능하다. 생성자를 통해 private 리소스에 접근하기 위해서 사용할 수있다.
학습목표
- 쓰레드는 무엇인가?
- Runnable 과 Thread의 차이는 무엇인가?
- synchronized는 무엇인가?
- volatile은 무엇인가?
쓰레드는 무엇인가??
java '클래스명.java' 명령어 입력시, 적어도 하나의 JVM이 시작된다. JVM이 시작되면 자바 프로세스가 시작한다. 이 프로세스라는 울타리 안에 여러개의 쓰레드가 존재하게 된다. 즉, 하나의 프로세스안에 여러개의 프로세스가 실행되며, 어떤 프로세스든 간에 쓰레드가 하나 이상 수행된다.
WAS를 사용하는 웹 애플리케이션도 마찬가지이다. WAS의 main()메소드에서 생성한 여러개의 쓰레드가 수행되는 것이다. 그렇다면 쓰레드는 왜 만들었을까??
→ 쓰레드가 무엇인지에 대한 정의가 없다.... 예전부터 느끼는 건데 책이 좀 불친절한 느낌.... 설명을 들어도 머릿속에 명확히 개념이 잡히지 않는다...
Process란?
단순히 실행 중인 프로그램이라고 할 수 있다. 즉, 사용자가 작성한 프로그램이 OS에 의해 메모리 공간을 할당받아 실행 중인 것을 말한다. 이런 프로세스는 프로그램에 사용되는 데이터와 메모리등의 자원 그리고 쓰레드로 구성된다.
그래서 쓰레드란 무엇인가?...
쓰레드(thread)란 프로세스(process) 내에서 실제로 작업을 수행하는 주체를 의미한다.
쓰레드는 왜 만들어졌나?
- 메모리 사용의 효율성 때문이다!
프로세스가 하나 시작하려면 많은 자원이 필요하다. 하나의 작업을 동시에 수행하려한다고 하자. 이때 여러개 의 프로세스를 띄워서 실행하려면 각각 메모리를 할당해 주어야한다. JVM은 기본적으로 32~64MB의 물리 메모리를 점유한다. 이에반해 쓰레드는 하나를 추가하면 1MB 이내의 메모리를 점유한다. 그래서 쓰레드를 경량 프로세스 라고도 부른다.
Runnable과 Thread의 차이는 무엇인가?
쓰레드를 생성하는 방법은 크게 2가지가 있다. 하나는 Runnable 인터페이스를 사용하는것, 다른 한가지는 Thread 클래스를 사용하는 것이다. Runnable에 선언되어 있는 메소드는 딱 하나다.
리턴타입 | 메소드 | 설명 |
void | run() | 쓰레드가 시작되면 수행되는 메소드 |
그럼 Runnable 인터페이스와 Thread클래스를 이용한 쓰레드 동작이 어떻게 다른지 비교해보자.
먼저 Runnable 인터페이스를 구현한 클래스를 만들어보자
public class RunnableExam implements Runnable{
public void run() {
// TODO Auto-generated method stub
System.out.println("Runnable");
}
}
다음은 Thread 클래스를 상속받은 클래스이다.
public class ThreadExam extends Thread{
public void run() {
System.out.println("Thread");
}
}
마지막으로 둘의 구현방식을 비교해보자.
public class RunThread {
public static void main(String[] args) {
// TODO Auto-generated method stub
RunThread rt = new RunThread();
rt.runBasic();
}
public void runBasic() {
//Runnable 인터페이스
RunnableExam re = new RunnableExam();
new Thread(re).start();
//Thread 클래스
ThreadExam te = new ThreadExam();
te.start();
}
}
/*
Thread
Runnable
*/
여기서 꼭 기억해야할 것이 2가지가 있다.
- 쓰레드가 수행되는 우리가 구현한 메소드는 run()이다.
- 쓰레드를 시작하는 메소드는 start()다.
우리는 쓰레드를 작성할때는 run()메소드를 시작점으로 작성해야한다. 그러나, 쓰레드를 시작하는 메소드는 start()이며 이는 자바에서 자동적으로 수행해준다.
그럼, 이어서 두가지 구현방식의 차이점을 알아보자.
new Thread(re).start();
Runnable 인터페이스를 구현한 RunnableExam 클래스는 바로 start()메소드를 사용할 수없다. 따라서 Thread 생성자에 RunnableExam객체를 추가한 후 사용할수 있다.
te.start();
이에 반해 Thread클래스를 상속받은 ThreadExam객체는 바로 start() 가 사용히 가능하다.
그럼 왜 이런 방식을 제공하는 걸까??
Runnable구현, Thread상속 2가지를 제공하는 이유
자바에서는 클래스를 하나만 상속받을 수 있다. 만약 어떤 클래스가 이미 extends를 사용해 확장하고 있는 상황이라면?? 이때 Runnable 인터페이스를 구현해 쓰레드를 구현하면 된다. 즉, 쓰레드 클래스를 확장할 수 없는 상태를 대비해서 2가지 방식으로 제공하는 것이다. 만약 클래스를 확장할 수 있는 상태라면 Thread 클래스를 확장하는 것이 객체생성으로 인한 메모리도 덜 쓸것이며, 편할 것이다.
Thread의 실행 순서는?
위의 코드에서 결과가 Thread → Runnable로 나왔다. 이상하지 않은가?? 분명 Runnable을 구현한 클래스의 start 메소드가 먼저 실행되었는데, 어째서 Thread가 먼저 출력됬을까?
한번더 실행해보자.
public class RunThread {
public static void main(String[] args) {
// TODO Auto-generated method stub
RunThread rt = new RunThread();
rt.runBasic();
}
public void runBasic() {
//Runnable 인터페이스
RunnableExam re = new RunnableExam();
new Thread(re).start();
//Thread 클래스
ThreadExam te = new ThreadExam();
te.start();
}
}
/*
Runnable
Thread
*/
순서가 바뀌었다! 왜 그런걸까?? 쓰레드를 start()를 통해 시작했다는 것은, 프로세스가 아닌 하나의 쓰레드를 JVM에 추가해 실행하는 것이다. new Thread(re).start(); 이 코드가 수행된 후 해당 코드가 수행이 다 끝나기를 기다리지 않는다. 바로 다음 코드로 넘어가 ThreadExam 객체를 생성한다. 그리고 마찬가지로 te.start(); 코드를 실행하고 runBasic()메소드는 te.start();의 수행이 끝났는지 확인하지 않고, 종료된다. → 즉 비동기식 방식이다!
그렇다면 새로 생성한 쓰레드는 언제 종료될까? 바로 run()메소드가 종료되면 쓰레드도 종료된다.
run()메소드가 끝나지 않으면 우리가 실행한 애플리케이션은 끝나지 않는다. 정말 끝나지 않는지를 확인하는 방법이 있으며 Thread클래스 메소드와 생성자를 공부하며 알아보자. 단, 여기서 예외적으로 데몬 쓰레드는 다르다. 이 또한 좀더 밑에서 알아보자.
Thread 클래스에 대해 알아보자
위의 생성자들을 보면 알수 있듯이. 쓰레드는 이름을 갖는다. 모든 쓰레드는 이름이 있다. 이름이 지정되지 않으면 "Thread-n"으로 명명된다. (n은 쓰레드가 생성된 순서에 따라 증가한다.) 또한 쓰레드는 이름이 겹쳐도 예외나 에러가 발생하지 않는다.
우리는 쓰레드를 생성할때 특정 그룹으로 묶을 수 있다. 그것이 바로 ThreadGroup이다. 이렇게 쓰레드를 그룹으로 묶으면 ThreadGroup에서 제공하는 메소드를 통해 여러가지 정보를 알아낼 수 있다.
→ 쓰레드에 더 알아본 추후에 알아보자
그리고 stackSize는 JVM의 런타임 데이터 영역에 생성되는 Stack에 관한 이야기이다. 자료구조 Stack을 말하는 것이 아니다!
많이 사용되는 sleep()메소드에 대해서 살펴보자.
Thread 클래스는 deprecated 된 메소드도 많고, static메소드도 많이 있다. 즉 Thread에 있는 static 메소드의 대부분은 해당 쓰레드를 위한 메소드가 아니라, JVM에 있는 쓰레드를 관리하기 위한 용도로 사용된다는 것이다. 물론 예외도 있으며 앞으로 살펴볼 sleep()이 그러하다.
앞서 run()메소드가 끝나지 않으면 애플리케이션은 끝나지 않는다고 했다. sleep메소드를 활용해 확인해 보자.
public class EndlessThread extends Thread {
public void run() {
while(true) {
try{
System.out.println(System.currentTimeMillis());
Thread.sleep(1000);
} catch(Exception e) {
e.printStackTrace();
}
}
}
}
/*
1622611975979
1622611976981
1622611977981
1622611978981
...
*/
한가지 sleep메소드를 사용할때 주의할 점은 항상 try-catch 구문으로 묶어줘야 한다. sleep()은 InterrupedException을 던질 수 도 있다고 선어되어 있기 때문이다.
위의 코드를 보면 알수 있듯이, main()메소드의 수행이 끝나도 쓰레드가 종료되지 않으면 자바 프로세스는 끝나지 않는다는 것을 알 수 있다.
Thread 클래스의 주요 메소드를 살펴보자.
여기서 보면 우선순위 라는것이 나왔다. 쓰레드의 우선순위란, 말그대로 대기하고 있는 상황에서 먼저 수행할 수 있는 순위를 말한다. 대부분 이값은 기본값을 권장한다.
쓰레드는 setDaemon(true)로 지정해줘야 데몬쓰레드로 인식이 된다. 이제 데몬쓰레드에 대해 알아보자.
데몬쓰레드란?
데몬쓰레드는 주 쓰레드 작업의 보조적인 역할을 수행하는 쓰레드이다.
데몬쓰레드가 아닌 사용자 쓰레드는 JVM이 해당 쓰레드가 끝날 때까지 기다린다고 했다. 그러나 데몬쓰레드는 이와는 다르게 데몬쓰레드가 끝이 나던, 말던 JVM이 종료될수 있다. 단 해당 쓰레드가 start하기 전에 지정되어야 한다.
다시말해서 데몬쓰레드는 해당 쓰레드가 종료되지 않아도, 다른 실행중인 일반 쓰레드가 없다면 멈춘다. 그럼 굳이 이런 데몬쓰레드는 왜 만들어졌을까?
데몬쓰레드는 왜 만들어졌을까??
쓰레드가 계속동작하고 있으면 프로세스는 끝나지 않는다. 만약 핵심업무를 수행하는 쓰레드와, 이를 보조하는 쓰레드가 있는데, 모두 일반 쓰레드로 선언하게된다면, 주 업무를 수행하는 쓰레드가 끝나면, 보조하는 쓰레드도 동작을 더 할 필요가 없다. 그러나, 일반 쓰레드이기 때문에 계속 동작을 할것이고, 이 때문에 프로세스는 주업무가 종료되었음에도 계속 유지 되어야한다. 이때 이런 보조작업을 수행하는 쓰레드를 데몬쓰레도 수행한다면?? 주 쓰레드가 종료시, JVM은 데몬쓰레드의 동작여부와 관계없이 종료할수 있게된다. (StrongReference와 WeakReferece의 관계와 비슷한것 같다.)
즉 이러한 특성때문에 데몬쓰레드는 주 쓰레드를 보조하는 쓰레드로 사용되며, 게시글 자동저장, 화면 자동갱신, 가비지컬렉터 등에 사용된다.
학습한 지식
- 자바의 객체란 무엇인가?
- 프로그램 상에서 구현할 대상
- 클래스에 선언된 정보 그대로 생성된 실체이다.
- enum은 무엇인가?
- 관련이 있는 상수들의 집합
- 생성자에서 사용하느 super()가 어떤 용도로 사용되는지?
- 자식클래스에서 부모클래스의 생성자를 호출하는 메소드
- 자식클래스라 할지라도 부모클래스의 priavate한 변수와 메소드에 접근이 불가능하다. 생성자를 통해 private 리소스에 접근하기 위해서 사용할 수있다.
- 쓰레드는 무엇인가?
- 쓰레드란 프로세스내에서 실질적으로 일을 처리하는 주체
- 쓰레드는 왜? 만들어졌으며 써야할까?? → 만일 어떤 작업을 동시에 처리해야한다고 하자. 프로세스를 2개 생성하는것은 자원 소모가 크다. JVM은 기본적으로 32~64MB의 물리적 메모리를 할당받는다. 이에 반해 쓰레드는 1MB이내의 메모리할당으로 가능하다.
- 프로세스 : OS로부터 메모리를 할당받아 동작하는 프로그램 그 자체
- Runnable 과 Thread의 차이는 무엇인가?
- 쓰레드를 생성하는 방식은 2가지로 Runnable 인터페이스 구현, Thread 클래스 상속이 있다.
- Runnable 인터페이스를 구현한 클래스도 start()메소드를 바로 사용할 수 없다. new Thread()의 매개변수로 해당 객체를 넣어줘야 가능하다.
- 자바의 상속은 다중상속이 불가능하다. 따라서 이미 다른클래스를 확장한 클래스를 쓰레드로 구현해야할 때 Runnable 인터페이스를 구현해 쓰레드로 구현하면 된다.
- Daemon쓰레드란?
- 일반 쓰레드와 달리 실행중이어도 JVM이 종료할수 있다.
- 주로 주 쓰레드의 업무를 보조하는 쓰레드로써, 주 쓰레드가 종료되면 보조쓰레드도 존재할 이유가 없으므로 데몬쓰레드를 이용한다.
- Strong Reference와 WeakReference와 비슷한 느낌이다.
Reference
[Java] 클래스, 객체, 인스턴스의 차이 - Heee's Development Blog
자바의신-VOL2
'프로그래밍 > Java' 카테고리의 다른 글
람다와 스트림의 사용법(2) (0) | 2021.06.10 |
---|---|
람다와 스트림의 사용법(1) (0) | 2021.06.10 |
Set과 Queue -Reboot(4) (0) | 2021.06.02 |
캐시 교체알고리즘 과 상황별 효율성 (0) | 2021.06.01 |
Map (0) | 2021.05.31 |