Back-end/이것이 자바다[신용권 한빛미디어]

작업 스레드 생성과 실행

Ho's log 2021. 11. 24. 23:26

멀티 스레드로 실행하는 애플리케이션을 개발하려면 먼저 몇 개의 작업을 병렬로 실행할지 결정하고 각 작업별로 스레드를 생성해야 한다.

 

어떤 자바 애플리케이션이건 메인 스레드는 반드시 존재하기 대문에 메인 작업 이외에 추가적인 병렬 작업의 수만큼 스레드를 생성하면 된다.

 

자바에서는 작업 스레드도 객체로 생성되기 때문에 클래스가 필요하다.

java.lang.Thread 클래스를 직접 객체화 해서 생성해도 되지만, Thread 를 상속해서 하위 클래스를 만들어 생성할 수도 있다. 

 

Thread 클래스로부터 직접 생성 


java.lang.Thread 클래스로부터 작업 스레드 객체를 직접 생성하려면 다음과 같이 Runnable을 매개값으로 갖는 생성자를 호출해야 한다

Thread thread = new Thread(Runnable target);

 

Runnalbe 은 작업 스레드가 실행할 수 있는 코드를 가지고 있는 객체라고 해서 붙여진 이름이다.

Runnable은 인터페이스 타입이기 때문에 구현 객체를 만들어 대입해야 한다. 

Runnable 에는 run()메소드 하나가 정의되어 있는데, 구현 클래스는 run()을 재정의 해서 작업 스레드가 실행할 코드를 작성해야 한다.

 

class Task implements Runnalbe {

	public void run()
    {
    	스레드가 실행할 코드;
    
    }
}

 

Runnable은 작업 내용을 가지고 있는 객체이지 실제 스레드는 아니다. 

Runnable 구현 객체를 생성한 후, 이것을 매개값으로 해서 Thread 생성자를 호출하면 비로소 작업 스레드가 생성된다.

Runnable task = new Task();

Thread thread = new Thread(Task);

 

코드를 좀더 절약 하기 위해 Thread 생성자를 호출 할 때 Runnalbe 익명 객체를 매개값으로 사용 할수 있다. 

이 방법이 많이 사용 

Thread thread = new Thread (new Runalbe() {
	public void run(){
    
    
    	스레드가 실행할 코드; 
    }

}) // 익명 구현 객체

 

Runnalbe 인테페이스 run() 메소드 하나만 정의되어 있기 때문에 함수적 인터페이스이다.

따라서 다음과 같이 람다식을 매개값으로 사용할 수도 있다.

가장 간단한 방법이지만, 자바 8부터 지원되기 때문에 자바 7 이전 버전에서는 사용할 수 없다. 

 

Thread thread - new Thread(() -> {

	스레드가 실행할 코드 ;; -

}) //-- 람다식

 

 

작업 스레드는 생성되는 즉시 실행되는 것이 아니라 , start() 메소드를 다음과 같이 호출해야만 비로소 실행된다

thread.start()

 

start() 메소드가 호출되면, 작업 스레드는 매개값으로 받은 Runnable의 run() 메소드를 실행하면서 자신의 작업을 처리한다

 

 

 

0.5 초 주기로 비프(beep)음을 발생시키면서 동시에 프린팅하는 작업이 있다고 가정

비프음 발생과 프린팅은 서로 다른 작업이므로 메인 스레드가 동시에 두 가지 작업을 처리 할 수 없다. 

import java.awt.*;

public class BeepPrintExample1 {

	public static void main(Stirng[] args){
    	Toolkit toolkit = Toolkit.getDefaultTookit(); -> Tookit 객체 얻기
        
        for(int i=0; i<5; i++){
        
        	toolkit.beep(); -> 비프음 발생 
            try{
            	Thread.sleep(500); - 0.5초간 일시 정지
            } catch(Exception e){
            
            }
        }
    	
        for(int i=0; i < 5; i++){
        	System.out.println("띵");
            try {
            	Thread.sleep(500);
            } catch(Exception e){
            }
        
        }
    
    
    }



}

 

비프음을 발생시키면서 동시에 프린팅을 하려면 두 작업 중 하나를 메인 스레드가 아닌 다른 스레드에서 실행

 

프린팅 - 메인 스레드

비프음 - 작업 스레드 

public class BeepTask implements Runnable{

	@Override
	public void run(){
    
    	Toolkit toolkit = Toolkit.getDefaultTookit();
        
        for(int i = 0 ; i < 5 ; i++){
        	toolkit.beep();
            try { Thread.sleep(500); 
            } catch(Exceptiom e ){}
        
        }
    
    }
}

 

BeepPrintExample1.java 에서 비프음을 발생하는 코드를 다음과 같이 작업 스레드 생성 및 실행 코드로 변경한다

 

public class BeepPrintExample2{

	public static void main(String[] args){
    
    	Runnalbe beepTask = new BeepTask;
        Thread thread = new Thread(beepTask);
        
        Thread thread = new Thread(beepTask);
    	thread.start();
        
        for(int i = 0; i < 5 ; i ++){
        	System.out.println("띵");
            
            try{ Thread.sleep(500); }
            catch(Exception e ){}
        
        
        
        }
    
    }




}

 

3라인에서 BeepTask 객체를 생성하고, 이것을 매개값으로 해서 4라인에서 작업 스레드를 생성한다.

5라인에서 작업 스레드의 start() 메소드를 호출하면 작업 스레드에 의해 BeepTask 객체의 run() 메소드가 실행되어 

비프음이 발생한다.

 

메인 스레드는 7라인의 for문을 실행시켜 0.5 초간격으로 "띵"을 프린팅 한다 

 

2가지의 또다른 방법 

//Runnable 익명 객체 이용
Thread thread = new Thread(new Runaable(){

	@Override
    public void run() {
    	Tookit toolkit = Toolkit.getDefaultToolkit();
        
        for(int i = 0; i < 5; i++){
        	toolkit.beep();
            try{Thread.sleep(500);} catch(Exception e){}
        }
    }


});

 

 

// 람다식 이용
Thread thread = new Thread(() -> {
	Toolkit toolkit = Toolkit.getDefaultToolkit();
    for(int i = 0 ; i < 5; i++){
    
    	toolkit.beep();
        try{ Thread.sleep(500); } catch(Exception e ){}
    }



});

 

 

Thread 하위 클래스로부터 생성


작업 스레드가 실행할 작업을 Runnable로 만들지 않고

Thread의 하위 클래스로 작업 스레드를 정의 하면서 작업 내용을 포함시킬 수도 있다.

작업 스레드 클래스를 정의하는 방법인데,

Thread 클래스를 상속한 후 run 메소드를 재정의(overriding)해서 스레드가 실행할 코드를 작성 하면 된다.

작업 스레드 클래스로부터 작업 스레드 객체를 생성하는 방법은 일반적인 객체를 생허나는 방법과 동일하다.

 

public class WorkerThread extends Thread {

	@Override
    public void run(){
    	// 스레드가 실행할 코드 
    
    }

}
 Thread thread = new WorkerThread();

 

코드를 좀 더 절약하기 위해 다음과 같이 Thread 익명 객체로 작업 스레드 객체를 생성할 수도 있다

 

Thread thread = new Thread(){

	public void run(){
    	스레드가 실행할 코드;
        
    }
}

 

이렇게 생성된 작업 스레드 객체에서 start() 메소드를 호출하면 작업 스레드는 자신의 run() 메소드를 실행하게 된다

thread.start();

 

 

 

Runnable을 생성하지 않고 Thread의 하위 클래스로 작업 스레드를 정의한 것 

 

import java.awt.Toolkit;

public class BeepThread extends Thread {

	@Overide
    public void run(){
    	Toolkit toolkit = Toolkit.getDefaulutToolkit();
        for(int i = 0 ; i<5; i ++){
        	toolkit.beep();
            try{
            	Thread.sleep(500);
            } catch(Exception e){
            
            }
        
        }
    
    
    }



}

 

 

//메인 스레드와 작업 스레드가 동시에 실행

public class BeepPrintExample3 {
	public static void main(String[] args){
    
    	Thread thread = new BeepThread();
        thread.start();
        
        for(int i =0; i < 5; i++){
        	System.out.println("띵");
            try{Thread.sleep(500);}
            catch(Exception e){}
        }
    
    
    }
    

}

 

3라인에서 BeepThread 객체를 생성하고, 4라인에서 start() 메소드를 호출하여 BeepThread가 run() 메소드를 실행

그와 동시에 메인 스레드는 6라인의 for 문을 실행시켜 0.5초 간격으로 "띵"을 프린팅.

3라인을 대체 해서 작업 스레드를 만들수 있는 또다른 방법

Thread thread = new Thread() {

	@Override
    public void run(){
    
    	Toolkit toolkit = Toolkit.getDefaultToolkit();
        for(int i=0; i<5; i++){
        
        	toolkit.beep();
            try{
            	Thread.sleep(500)
            } catch (Excepiton e ){}
        }
    
    }
}

 

 

스레드의 이름 


스레드는 자신의 이름을 가지고 있다

스레드의 이름은 디버깅할때 어떤 스레드가 어떤 작업을 하는지 조사할 목적으로 사용 

메인스레드는 "main" 이라는 이름

우리가 직접 생성한 스레드는 자동적으로 "Thread -n" 이라는 이름으로 설정

n은 스레드의 번호 

 

다른 이름 으로 설정 하고 싶다면 Thread클래스의 setName() 메소드로 변경

thread.setName("스레드 이름");

 

반대로 스레드 이름을 알고 싶은 경우에는 getName() 메소드를 호출

thread.getName();

 

setName()과 getName()은 Thread의 인스턴스 메소드임로 스레드 객체의 참조가 필요

 

만약 스레드 객체의 참조를 가지고 있찌 않다면,

Thread의 정적 메소드인 currentThread()로 코드를 실행하는 현재 스레드의 참조를 얻을수 있따.

Thread thread = Thread.currentThread();

 

public class ThreadNameExample {

	public static void main(String[] args){
    	
        Thread mainThread = Thread.currentThread(); --> 이 코드를 실행하는 스레드 객체 얻기
        System.out.println("프로그램 시작 스레드 이름 :" + mainThread.getName());
        
        ThreadA threadA = new ThreadA();
        System.out.println("작업 스레드 이름: " + threadA.getName());
        threadA.start(); --> 스레드 a 시작 
        
        ThreadA threadB = new ThreadB();
        System.out.println("작업 스레드 이름: " + threadB.getName());
        threadB.start(); --> 스레드 b 시작
    
    
    
    }




}

 

 

public class ThreadA extends Thread {
	public ThreadA() {
    	setName("ThreadA");
    }
    
    
    public void run(){
    	for(int i=0; i<2; i++){
        	System.out.println(getName() + "가 출력한 내용")
        }
    
    }

}
public class ThreadB extends Thread {

    
    public void run(){
    	for(int i=0; i<2; i++){
        	System.out.println(getName() + "가 출력한 내용")
        }
    
    }

}

'Back-end > 이것이 자바다[신용권 한빛미디어]' 카테고리의 다른 글

동기화 메소드와 동기화 블록  (0) 2021.12.04
스레드 우선순위  (0) 2021.11.27
멀티 스레드 개념  (0) 2021.11.22
java.time 패키지  (0) 2021.10.25
Format 클래스  (0) 2021.10.24