본문 바로가기

java

Step15_Thread (다양한 방식으로 Thread 만들기)

 

 

Frame01 (Thread 개요)

 

package test.main;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;


public class Frame01 extends JFrame implements ActionListener{
   
   //생성자
   public Frame01() {
      //레이아웃 설정 
      setLayout(new BorderLayout());
      //페널을 프레임의 상단에 배치 
      JPanel panel=new JPanel();
      panel.setBackground(Color.YELLOW);
      add(panel, BorderLayout.NORTH);
      //버튼을 페널에 추가 하고 
      JButton alertBtn=new JButton("알림 띄우기");
      panel.add(alertBtn);
      //버튼에 리스너 등록하기
      alertBtn.addActionListener(this);
   }
   
   public static void main(String[] args) {
      //MyFrame 클래스를 이용해서 객체 생성하고 참조값을 지역변수 frame 에 담기 
      Frame01 frame=new Frame01();
      //프레임의 제목 설정
      frame.setTitle("Frame01");
      //프레임을 닫으면 자동으로 프로세스가 종료 되도록 한다.
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setBounds(100, 100, 500, 500);
      frame.setVisible(true);
   }

   @Override
   public void actionPerformed(ActionEvent arg0) {
      JOptionPane.showMessageDialog(this, "알림~~~");
      
      System.out.println("알림창이 닫아 졌습니다.");
      
      try {
         System.out.println("10 초동안 무언가 준비 작업을 해요");
         Thread.sleep(10000);
      } catch (InterruptedException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
      System.out.println("actionPerformed() 메소드가 리턴됩니다.");
   }
}

 

@override (재정의) 한 actionPerformed 부분을 살펴보자.

JOptionPane.showMessageDialog(this, "알림~~~");

실행을 시켜보면 위의 코드인 showMessageDialog가 뜨고 확인을 누를 때까지 다음 작업이 실행되지 않는다.

System.out.println("알림창이 닫아 졌습니다.");
      try {
         System.out.println("10 초동안 무언가 준비 작업을 해요");
         Thread.sleep(10000);
      } catch (InterruptedException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }

확인을 누르면 위의 코드에 관한 작업이 실행되는데 여기서 Thread.sleep(10000); 즉 10초 지연된다.

System.out.println("actionPerformed() 메소드가 리턴됩니다.");

10초 이후에나 해당 작업이 진행된다.

 

여기서 알 수 있는 것이 현재 작업할때 사용하는 Thread(흐름)이 한개라는 것을 알 수 있다. 추후에 여러개를 사용할 수 있어야한다.

 

 

Frame02, CountThread (Thread를 상속받아 새로운 Thread 만들기)

 

Frame02 

package test.main;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import test.mypac.CountThread;

public class Frame02 extends JFrame implements ActionListener{
   
   //생성자
   public Frame02() {
      //레이아웃 설정 
      setLayout(new BorderLayout());
      //페널을 프레임의 상단에 배치 
      JPanel panel=new JPanel();
      panel.setBackground(Color.YELLOW);
      add(panel, BorderLayout.NORTH);
      //버튼을 페널에 추가 하고 
      JButton countBtn=new JButton("1~10 까지 세기");
      panel.add(countBtn);
      //버튼에 리스너 등록하기
      countBtn.addActionListener(this);
   }
   
   public static void main(String[] args) {
      //MyFrame 클래스를 이용해서 객체 생성하고 참조값을 지역변수 frame 에 담기 
      Frame02 frame=new Frame02();
      //프레임의 제목 설정
      frame.setTitle("Frame02");
      //프레임을 닫으면 자동으로 프로세스가 종료 되도록 한다.
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setBounds(100, 100, 500, 500);
      frame.setVisible(true);
   }

   @Override
   public void actionPerformed(ActionEvent arg0) {
      /*
       *  스레드 객체는 1회용이다.
       *  한번 run() 메소드가 리턴하면 그 객체는 다시 사용할수 없다.
       */
     //CountThread 객체를 생성해서
     CountThread t=new CountThread();
     //새로운 스레드 시작 시키기
     t.start(); //start() 메소드는 run() 메소드 리턴과 상관없이 바로 리턴하기 때문에
     //new CountThread().start(); 위에꺼 짧게 줄여쓰기
     
     //아래의 내용이 즉시 콘솔창에 출력된다.
     System.out.println("새로운 스레드가 시작 되었습니");
   }
}

CountThread

package test.mypac;
/*
 *    새로운 스레드 만드는 방법
 * 
 *    1. Thread 클래스를 상속 받은 클래스를 정의한다.
 *    2. run() 메소드를 오버라이드 한다.
 *    3. run() 메소드 안에서 새로운 스레드에서 해야 할 작업을 코딩한다.
 *    4. 만든 클래스로 객체를 생성하고 해당 객체의 start() 메소드를 호출하면 새로운 스레드가
 *       시작된다.
 */
public class CountThread extends Thread{
   
   @Override
   public void run() {
      //run 메소드 안쪽이 새로운 작업 단위가 된다.
      int count=0;
      while(true) {
         try {
            Thread.sleep(1000);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
         count++;
         System.out.println("현재 카운트:"+count);
         if(count==10) {
            break;
         }
      }
   }
}

 

새로운 스레드 만드는 방법 1 

 

1. Thread 클래스를 상속 받은 클래스를 정의한다.
2. run() 메소드를 오버라이드 한다.
3. run() 메소드 안에서 새로운 스레드에서 해야 할 작업을 코딩한다.
4. 만든 클래스로 객체를 생성하고 해당 객체의 start() 메소드를 호출하면 새로운 스레드가 시작된다.

 

Thread 객체 사용 특징

 

Thread 객체는 1회용이다.
한번 run() 메소드가 리턴하면 그 객체는 다시 사용할수 없다.

 

java에서 멀티 thread를 사용할 경우 한개당 약 3mb정도가 사용될 정도로 무거운 작업이다.

멀티 thread를 사용한다고 해서 thread 한개가 작동하는 동안 타 thread가 같이 작동하는 것이 아니라 빠른 속도로 돌아가면서 작동하기 때문에 작업 속도가 증가하는 것은 아니다.

추가로 javascript는 싱글 thread를 사용하기 때문에 비슷한 작업을 할때 무겁게 작업되지 않는다.

 

 

Frame 03 CountRunnable (Runnable 인터페이스를 구현하여 Thread 만들기)

 

Frame 03 

package test.main;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import test.mypac.CountRunnable;
import test.mypac.CountThread;

public class Frame03 extends JFrame implements ActionListener{
   
   //생성자
   public Frame03() {
      //레이아웃 설정 
      setLayout(new BorderLayout());
      //페널을 프레임의 상단에 배치 
      JPanel panel=new JPanel();
      panel.setBackground(Color.YELLOW);
      add(panel, BorderLayout.NORTH);
      //버튼을 페널에 추가 하고 
      JButton countBtn=new JButton("1~10 까지 세기");
      panel.add(countBtn);
      //버튼에 리스너 등록하기
      countBtn.addActionListener(this);
   }
   
   public static void main(String[] args) {
      //MyFrame 클래스를 이용해서 객체 생성하고 참조값을 지역변수 frame 에 담기 
      Frame03 frame=new Frame03();
      //프레임의 제목 설정
      frame.setTitle("Frame02");
      //프레임을 닫으면 자동으로 프로세스가 종료 되도록 한다.
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setBounds(100, 100, 500, 500);
      frame.setVisible(true);
   }

   @Override
   public void actionPerformed(ActionEvent arg0) {
     
     Thread t=new Thread(new CountRunnable());
     t.start();
     //아래의 내용이 즉시 콘솔창에 출력된다.
     System.out.println("새로운 스레드가 시작 되었습니다");
   }
}

CountRunnable

package test.mypac;
/*
 *   새로운 스레드를 만드는 방법2
 *   
 *   1.  Runnable 인터페이스를 구현한 클래스를 정의한다.
 *   2.  run() 메소드를 강제 오버라이드 한다.
 *   3.  Thread 클래스로 객체를 생성하면서 해당 클래스로 만든 객체를 생성자의 인자로 전달한다.
 *   4.  Thread 클래스로 만든 객체의 start() 메소드를 호출해서 스레드를 시작시킨다.
 */
public class CountRunnable implements Runnable{

   @Override
   public void run() {
      //run 메소드 안쪽이 새로운 작업 단위가 된다.
      int count=0;
      while(true) {
         try {
            Thread.sleep(1000);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
         count++;
         System.out.println("현재 카운트:"+count);
         if(count==10) {
            break;
         }
      }
   }
}

 

새로운 스레드를 만드는 방법2

 

1.  Runnable 인터페이스를 구현한 클래스를 정의한다.

2.  run() 메소드를 강제 오버라이드 한다.

3.  Thread 클래스로 객체를 생성하면서 해당 클래스로 만든 객체를 생성자의 인자로 전달한다.

4.  Thread 클래스로 만든 객체의 start() 메소드를 호출해서 스레드를 시작시킨다.

 

다만 Runnable 인터페이스는 추상 메소드 run()을 가지고 있는 것에 불과하다. 1번 방법에서 thread 클래스를 상속 받았을때의 특별한 경우와는 다르다. 따라서 적용 방식이 조금 다르다.

 

 

Frame 04~06 (익명클래스 사용, 함수표현식 사용)

 

같은 맥락으로 Runnable 인터페이스를 익명클래스, 함수표현식으로 사용할 수도 있다. 

 

미리 만들어 둔 CountThread 혹은 CountRunnable 을 사용하지 않고 익명클래스를 사용하여 다양한 형태로 표현해보자

 

Frame 04 익명클래스 사용

Frame 05 변수에 담지 않고 사용

Frame 06 함수 표현식 사용