classBlue{
publicvoidrun() {
while(true) {
System.out.println("청기 올려!!!");
}
}
}
classWhite{
publicvoidrun() {
while(true) {
System.out.println("백기 올려!!!");
}
}
}
publicclassMain{
publicstaticvoidmain(String[] args) {
White white = new White();
Blue blue = new Blue();
// CPU는 코드를 순차적으로 실행하므로, 첫번째 호출한 run 메서드만 무한반복함
white.run();
blue.run();
}
}
각각의 깃발을 드는 클래스의 객체를 생성한 후에
메서드를 호출하면
"백기 올려"만 출력됨
즉, CPU는 순차적으로 코드를 실행하므로, 첫번째 호출한 white.run()만 무한 반복
[ 실행 결과 ]
[ 예제 ] Thread 클래스를 이용해 청기 백기 클래스 구현하기
// Thread 클래스를 받속 받기classBlueextendsThread{
@Override
public void run() { // run() 메서드를 오버라이딩 해서 메시지를 무한 출력while(true) {
System.out.println("청기 올려!!!");
}
}
}
//Thread 클래스를 받속 받기classWhiteextendsThread{
@Override
public void run() { // run() 메서드를 오버라이딩 해서 메시지를 무한 출력while(true) {
System.out.println("백기 올려!!!");
}
}
}
publicclassMain{
publicstaticvoidmain(String[] args){
// 실행 클래스에서 각각의 스레드 객체 생성한 후, 스레드를 실행
White white = new White();
Blue blue = new Blue();
// 스레드를 실행하기 위해서는 run() 메서드가 아닌// start() 메서드를 호출하여 실행해야함
white.start();
blue.start();
// white.start(); // 종료된 쓰레드는 다시 실행 불가, 예외 발생
}
}
실행 클래스에서 각각의 쓰레드 클래스(White, Blue) 인스턴스를 생성한 후, 스레드를 실행
스레드를 실행하기 위해서는
run() 메서드가 아닌, start() 메서드를 호출하여 실행해야함
한번 실행이 종료된 쓰레드는 다시 실행할 수 없음
white.start();
white.start(); // 종료된 쓰레드는 다시 실행 불가, 예외 발생
[ 실행 결과 ]
Runnable 인터페이스를 구현하여 스레드 만들기
Runnable 인터페이스를 구현하는 클래스는
Runnable 인터페이스가 가지고 run() 메서드를 오버라이딩하여, 쓰레드 실행 시에 수행할 작업을 구현
Runnable 인터페이스는 언제 사용하나?
자바는 다중 상속이 되지 않기 때문에
이미 다른 클래스의 상속을 받고 있다면 Thread 클래스를 상속하여 사용할 수 없음
이렇게, Thread 클래스를 바로 상속받는 것이 어려운 경우에 사용하면 유용함
Runnable 인터페이스를 구현하여 사용자 정의 스레드 클래스 생성 및 실행
1) 쓰레드 구현
publicclassThreadInterfaceimplementsRunnable{
@Overridepublicvoidrun(){
// 스레드 작업 내용
}
}
java.lang.Runnable 인터페이스를 구현하는 클래스 만들기
run() 메서드를 오버라이딩하여, 스레드의 기능을 구현
2) 쓰레드 실행
publicclassMain{
publicstaticvoidmain(String[] args){
ThreadInterface i = new ThreadInterface();
Thread t = new Thread(i);
t.start();
}
}
Thread 클래스를 상속받고 있지 않기 때문에, start() 메서드를 사용할 수 없음
따라서, Runnable 인터페이스를 구현한 클래스의 인스턴스를 만들고
Thread 클래스의 인스턴스를 생성할 때, 생성자의 매개변수로 (Runnable 인터페이스를 구현한 클래스의 인스턴스)를 넘겨주고
그 후에 쓰레드 Thread 클래스의 start() 메서드를 사용할 수 있음
[ 예제 ] Runnable 인터페이스를 이용하여 '청기백기 올리기 기능' 구현한 스레드
//Runnable 인터페이스 구현하는 클래스 만들기classBlueimplementsRunnable{
@Overridepublicvoidrun(){ // run() 메서드를 오버라이딩 해서 메시지를 무한 출력while(true) {
System.out.println("청기 올려!!!");
}
}
}
//Runnable 인터페이스 구현하는 클래스 만들기classWhiteimplementsRunnable{
@Overridepublicvoidrun(){ // run() 메서드를 오버라이딩 해서 메시지를 무한 출력while(true) {
System.out.println("백기 올려!!!");
}
}
}
publicclassTest{
publicstaticvoidmain(String[] args){
// 인터페이스를 구현한 클래스 객체를 생성
White white = new White();
Blue blue = new Blue();
// Thread 객체 생성 시, 반드시 생성자의 인자로 구현 클래스 객체를 전달
Thread t = new Thread(white);
Thread t2 = new Thread(blue);
// 참조변수로 start() 메서드를 호출하여 메서드 실행
t.start();
t2.start();
}
}
Runnable 인터페이스를 구현한 클래스는 완전한 쓰레드 클래스가 아님
Thread 클래스를 상속받지 않았기 때문에, 쓰레드의 기능을 사용할 수 없고
따라서, start() 메서드도 없음
즉, Runnable 인터페이스의 run() 메서드만 오버라이딩하여 구현한 상태
쓰레드 기능을 사용하기 위해서는 반드시 Thread 클래스의 객체를 이용하여야 함
Thread 클래스의 객체 생성시에, 반드시 생성자의 인자로 구현 클래스 객체(white, blue)를 전달하여 쓰레드를 생성
Thread 클래스 타입의 참조 변수를 통해서 쓰레드 기능을 사용할 수 있음
쓰레드를 실행하고 싶을 때는
참조 변수로 start() 메서드를 호출하여, 쓰레드 기능을 사용
t.start();
[ 실행 결과 ]
start()와 run()의 차이
쓰레드 클래스에는 run() 메서드를 오버라이딩 하여 쓰레드가 실행할 내용을 정의하는데, 쓰레드를 실행할 때는 왜 run()메서드가 아닌 start() 메서드를 써서 실행을 할까?
1. run() 메서드
run() 메서드를 직접 호출하면
현재의 쓰레드인 메인 쓰레드(Main Thread) 내에서 호출이 됨
즉, 메인 쓰레드(Main Thread)의 호출 스택 1개만 이용하는 것
메인 쓰레드(Main Thread) 1개만을 이용한다는 것은
별도의 쓰레드를 만들어서 해당 메서드를 실행하는 것이 아니므로
쓰레드의 활용이 아님
즉, run() 메서드를 호출한다는 것은, 메인 쓰레드에서 Thread 객체의 메소드를 호출하는 것에 불과함뿐
[ 호출 스택 ] - 이 영역은 실질적인 명령어를 담고 있는 메모리로, 명령어를 하나씩 꺼내서 실행시키는 역할
- 만약, 동시에 2가지 작업을 한다면, 2개 이상의 호출 스택이 필요하게 됨 - 쓰레드를 사용한다는 것은, JVM이 여러 개의 호출 스택을 번갈아가면서 일처리를 하여 - 사용자에게 동시에 작업하는 것처럼 보여주는 것
2. start() 메서드
메인 쓰레드(Main Thread)와 별도의 쓰레드로 실행을 시키려면 JVM의 도움이 필요하기 때문에 Start() 메서드를 호출함
자바 프로그램을 실행하면, main 쓰레드가 실행되고, main() 메서드가 호출 됨
main() 메서드 내에서 start() 메서드를 호출
start() 메서드를 호출하면
JVM이 새로운 쓰레드를 생성하고
새로운 쓰레드가 작업을 실행하는데 사용하는 호출 스택(call stack)이 생성되고
새로 생성된 호출 스택에서 run() 메서드가 호출이 됨
이로 인하여,생성된 호출 스택에 run() 메서드가 첫번째로 올라가게 함
쓰레드는 독립된 공간에서 작업을 수행함
이제는 호출 스택이 2개이므로, 스케줄러가 정한 순서에 따라서 번갈아가면서 실행
쓰레드 객체를 start() 할때마다, 새로운 호출 스택(Call Stack)이 생성되고 작업이 완료된 이후에는 호출 스택이 소멸됨
익명 스레드 객체와 익명 스레드 자식 객체로 스레드 사용하기
[ 언제 사용하는가? ] - 화면에서 단발성으로 스레드를 사용하는 경우
- 채팅 중에 사용되는 파일 전송 같은 기능 어쩌다 한번씩 사용하는 기능 - 즉, 재사용되지 않는 스레드 작업을 수행하고 싶을 때, 익명 스레드 객체와 익명 스레드 자식 객체를 사용하면 편리함
1. Thread 클래스를 상속받는 익명 클래스로 '파일 업로드 기능' 구현
익명 스레드 자식 객체(상속 받는 익명 클래스)
classBlueimplementsRunnable{
@Overridepublicvoidrun(){
while(true) {
System.out.println("청기 올려!!!");
}
}
}
classWhiteimplementsRunnable{
@Overridepublicvoidrun(){
while(true) {
System.out.println("백기 올려!!!");
}
}
}
publicclassMain{
publicstaticvoidmain(String[] args){
White white = new White();
Blue blue = new Blue();
Thread t1 = new Thread(white);
Thread t2 = new Thread(blue);
t1.start();
t2.start();
// Thread 클래스를 상속받는 익명 클래스를 이용하여// run() 메서드를 오버라이딩하여 스레드 구현
Thread thread = new Thread() {
@Overridepublicvoidrun(){
for (int i = 0; i < 1000000; i++) {
System.out.println("파일 업로드 중!");
}
}
};
thread.start();
}
}
[ 실행 결과 ]
2. Runnable을 구현한 익명 스레드 객체를 이용하여 '파일 업로드 기능' 구현
안드로이드 화면 기능에서 많이 사용
publicclassMain{
publicstaticvoidmain(String[] args){
White white = new White();
Blue blue = new Blue();
Thread t1 = new Thread(white);
Thread t2 = new Thread(blue);
t1.start();
t2.start();
// Runnable 인터페이스를 구현한 익명 스레드 객체를 이용하여// 스레드로 사용new Thread( new Runnable() {
@Overridepublicvoidrun(){
for (int i = 0; i < 1000000; i++) {
System.out.println("파일 업로드 중입니다");
}
}
}).start();
}
}