국비교육(22-23)

21일차(1)/java(28) : Thread

서리/Seori 2022. 11. 3. 23:53

21일차(1)/java(28) : Thread

 

Step15_Thread

 

패키지01 MyFrame

- sleep(스레드는 멈추는 메소드)을 사용한 경우

package frame01;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;

public class MyFrame extends JFrame implements ActionListener{
	
	//생성자
	public MyFrame(String title) {
		super(title);
		//프레임의 초기 설정 작업하기
		setBounds(100,100,500,500);
		//이 프레임의 x버튼(close버튼)을 눌렀을때 프로세스도 같이 종료되도록 설정
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//레이아웃 설정
		setLayout(new FlowLayout());
		
		JButton startBtn=new JButton("작업 시작");
		startBtn.addActionListener(this);
		//프레임에 버튼 추가		
		add(startBtn);
				
		JTextField tf=new JTextField(10);
		//프레임에 JTextField 추가
		add(tf);
		
		//프레임을 화면상에 실제 보이게 하기(false하면 화면에 보이지않는다.)
		this.setVisible(true);
	}	
	
	//run 했을때 실행의 흐름이 시작되는 특별한 메소드
	public static void main(String[] args) {
		//MyFrame 객체 생성하기
		new MyFrame("나의 프레임");
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		System.out.println("작업을 시작합니다.");
		
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e1) {			
			e1.printStackTrace();
		}
		
		System.out.println("작업이 끝났습니다.");
	}
}


- this. 생략 가능
- 버튼동작을 위해서는 액션리스너를 넣어야 하는데, 프레임에 구현해서 하기로 함

- 중간에 임의로 시간이 오래걸리는 작업을 넣어보면(sleep), 동작이 10초간 강제로 멈추는 것을 볼 수 있다.
 

- sleep을 작성하면 밑줄 생김. surrounded with try/catch 클릭해서 try~catch 문으로 묶어주기.

 

- 실행해보면 sleep에서 10초간 잡혀 있다.

- actionPerformed에서 리턴하는 데까지 10초가 걸린다.

 

- 프레임 창의 버튼도 눌러지지 않고 완전히 멈춘 상태!

 

- run 했을때 코드를 한줄씩 실행하는 흐름을 스레드Thread 라고 한다.

- 메인메소드에서 시작되는 스레드를 main thread (기본작업단위) 라고 한다.

 

 

- 스레드가 처음 시작되어서 메인메소드를 빠져나오면,

 완전히 종료되지 않고 어딘가에서 대기를 하고 있다가 (stay)

 버튼이 클릭되면 actionPerformed 위치에 들어가서 실행되는 것이다!

- 10초간 잡혀있다가 끝나면 다시 stay 상태로 돌아간다.

 

- stay 상태에선 무엇을 하는가? 사용자의 입력, 반응에 대해서 기다리고 있음

- 버튼을 누르거나, 뭔가를 입력하면 거기에 해당되는 작업을 처리해준다.

 

- 그런데 지금은 버튼을 누르면 정지 발생. 10초간 잡혀있어서 다른 동작을 할 수 없다.

- 메인스레드가 사용자가 입력한 키를 TextField에 출력해주는 일도 했던 것!

 

- 이처럼 시간이 오래 걸리는 불확실한 작업을 하게 되면 UI의 응답성이 떨어진다.

 

- 그러면 스레드, 작업단위를 여러 개만들면 되지 않나?

→ 멀티스레딩 multithreading : 독립적으로 작업하는 작업단위를 원하는만큼 여러 개 만드는 것.

 

ex) 어떤 홈페이지에 방문, 웹서핑, 다운로드, ...  할 때

 한 명의 클라이언트가 접속했다고 해서 다른사람이 접속 못하지 않는다. 여러 사용자가 동시에 접속할 수 있다.

클라이언트가 접속하면 그 사이트에서 그 클라이언트를 처리하기 위한 하나의 스레드를 만들어주는 것.

 


 

- 스레드를 만드는 방법은 두 가지가 있다.
1) Thread를 상속받아서
2) 인터페이스를 구현해서

 

 

패키지02 MyFrame

 

- Thread 를 상속받아서 만드는 스레드

- 메인스레드가 아닌 다른 스레드에서 작업! 10초 잡힌 동안에도 다른 활동을 할 수 있다.

package frame02;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;

import test.mypac.AnotherThread;

public class MyFrame extends JFrame implements ActionListener{
	
	//생성자
	public MyFrame(String title) {
		super(title);
		//프레임의 초기 설정 작업하기
		setBounds(100,100,500,500);
		//이 프레임의 x버튼(close버튼)을 눌렀을때 프로세스도 같이 종료되도록 설정
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//레이아웃 설정
		setLayout(new FlowLayout());
		
		JButton startBtn=new JButton("작업 시작");
		startBtn.addActionListener(this);
		//프레임에 버튼 추가		
		add(startBtn);
				
		JTextField tf=new JTextField(10);
		//프레임에 JTextField 추가
		add(tf);
		
		//프레임을 화면상에 실제 보이게 하기(false하면 화면에 보이지않는다.)
		this.setVisible(true);
	}	
	
	//run 했을때 실행의 흐름이 시작되는 특별한 메소드
	public static void main(String[] args) {
		//MyFrame 객체 생성하기
		new MyFrame("나의 프레임");
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		System.out.println("새로운 스레드를 시작합니다.");
		//스레드 객체를 생성해서
		Thread t=new AnotherThread();
		/*
		 * start() 메소드를 호출하면 새로운 스레드가 시작된다.
		 * 새로운 스레드(작업단위)는 위에서 생성한 스레드 객체의 run()메소드가 자동으로 호출되면서 시작된다.
		 * run() 메소드의 리턴 여부와 상관없이 start() 메소드는 바로 리턴된다.
		 * start() 메소드를 호출한 스레드는 main 스레드이기 때문에 main 스레드는 또 다른 작업을 할 수가 있다.
		 */
		t.start();
	}
}

 

 

test.mypac - AnotherThread 클래스 생성

package test.mypac;
/*
 * 새로운 스레드 만드는 방법
 * 
 * 1. Thread 클래스를 상속받은 클래스를 정의한다.
 * 2. run() 메소드를 오버라이드 한다.
 * 3. run() 메소드 안에서 새로운 스레드에서 해야 할 작업을 코딩한다.
 * 4. 만든 클래스로 객체를 생성하고 해당 객체의 start() 메소드를 호출하면 새로운 스레드가 시작된다.
 */
public class AnotherThread extends Thread{//1.
	//2.
	@Override
	public void run() {
		System.out.println("작업을 시작합니다.");
		
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e1) {			
			e1.printStackTrace();
		}
		
		System.out.println("작업이 끝났습니다.");
	}
}

 

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

 

- 스레드를 상속한 후 ctrl+space 하면 부모가 갖고있는 메소드도 전부 보인다.

- 특정 메소드를 재정의하고 싶으면 오버라이드. run() 을 오버라이드하기!

 

 

- 메인메소드에서 메인스레드가 시작되고, run 메소드에서는 또다른 스레드가 실행된다.

- AnotherThread 객체의 run 메소드에서 새 스레드가 시작되는 것

- AnotherThread 객체는 필요할 때마다 여러 개 만들 수 있다. new AnotherThread(); 할 때마다 만들어지는 것!
- 각각의 객체는 run(){} 메소드가 독립적으로 존재하는 것이다.
 이 객체 안의 run이 각각 따로 작동한다!

- run으로 호출하는 것이 아니라 start 메소드를 호출해야 작동한다.

 

- new AnotherThread().start(); 를 넣고 run 해보면 

버튼을 눌러서 sleep되어 있는 동안에도 문자 입력이 가능하다.

 

- 클래스의 설계 필요 → 객체 생성 start로 호출해서 간접적으로 run 이 호출되도록 하는 것.

 

- 또한 각각의 스레드가 독립적으로 작업되기때문에 sleep된 10초 이내에 작업을 여러번 반복해 시작하는 것도 가능하다.

 

 

new AnotherThread().start();
//
Thread t=new AnotherThread();
t.start();

- 같은 식이다.

- start 메소드는 즉시 시작한다.(바로 리턴된다)

- 스레드 객체는 재활용이 안 된다. run메소드가 한번 리턴하면 그냥 버리는 것.(일회성 객체)
- run객체를 어딘가에 담아놓고 start만 여러 번 호출한다고 스레드가 여러 개 만들어지지 않는다.
 반드시 필요할 때마다 new해서 만들어야 한다. 

 

 


 

mypac 패키지 / CountThread 클래스 생성

package test.mypac;

public class CountThread extends Thread{
	@Override
	public void run() {
		//카운트 값을 저장할 지역변수를 만들고 초기값 대입
		int count=10;
		while(true) {
			System.out.println("현재 카운트:"+count);
			if(count==0) {//만일 count가 0이면
				break; //반복문 탈출(반복문 탈출하면 run() 메소드가 리턴되기 때문에 스레드가 종료된다.
			}
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {			
				e.printStackTrace();
			}
			count--;
		}
	}
}

 

패키지03 MyFrame

package frame03;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;

import test.mypac.AnotherThread;
import test.mypac.CountThread;

public class MyFrame extends JFrame implements ActionListener{
	
	//생성자
	public MyFrame(String title) {
		super(title);
		//프레임의 초기 설정 작업하기
		setBounds(100,100,500,500);
		//이 프레임의 x버튼(close버튼)을 눌렀을때 프로세스도 같이 종료되도록 설정
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//레이아웃 설정
		setLayout(new FlowLayout());
		
		JButton startBtn=new JButton("카운트 다운 시작");
		startBtn.addActionListener(this);
		//프레임에 버튼 추가		
		add(startBtn);
				
		JTextField tf=new JTextField(10);
		//프레임에 JTextField 추가
		add(tf);
		
		//프레임을 화면상에 실제 보이게 하기(false하면 화면에 보이지않는다.)
		this.setVisible(true);
	}	
	
	//run 했을때 실행의 흐름이 시작되는 특별한 메소드
	public static void main(String[] args) {
		//MyFrame 객체 생성하기
		new MyFrame("나의 프레임");
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		System.out.println("카운트 다운을 시작합니다.");
		new CountThread().start();
	}
}

 

- 카운트다운할 때마다 새로운 스레드에서 카운팅 작업을 하는 것
- for문은 인덱스 값이 있을 때 주로 쓰고, 없다면 while문이 더 편할 수 있다.
- count--; : 카운트를 1씩 감소시키기.

 

- run은 호출한것뿐 시작되지 않는다. start(); 가 있어야 새 스레드 시작!

 


 

Q) 멀티스레드는 한번에 여러가지 일을 할 수 있는데 일의 개수에는 제한이 없는지?

A) 스레드 1개당 1~2MB의 메모리 소모! 
  메모리의 용량에 따라 달라진다. 스레드는 좀 무거운 편이다.

 

Q) 일회성 객체를 만들어야한다는 것은 따로 클래스를 만들어야한다는 뜻인지?

A) 일회성 클래스가 아니고 객체!
 Thread t=new AnotherThread(); 를 필드에 하나만 만들어놓고 반복해서 쓸 수 없다. 매번 새로 생성해야한다!
 new한 객체의 start() 는 딱 한번만 사용 가능하다.

 


 

[ 멀티스레드 vs 멀티 프로세싱 ]



멀티스레드는 하나의 프로세스에서 작업단위만 여러 개 만든 것이다.
즉 2개 이상의 스레드가 동시에 진행되지는 않는다.
하나의 스레드가 작업중일 때늗 다른 스레드는 일시정지 상태가 된다.
여러 개의 스레드가 무작위 순서로 무작위 시간만큼 아주 빠르게 번갈아가면서 진행이 된다.
따라서 겉으로 보기에는 동시에 진행되는 것처럼 보이기도 한다.
 
ex) 두개의 선을 동시에 그린다고 하자. 각각의 선을 하나의 스레드라고 하면, 한 손으로 조금씩 조금씩 양쪽 선을 그리는 것. 

이것을 아주 빠른 속도로 교대로 반복하다보니 두개가 동시에 진행되는 것처럼 보이는 것이다. 이것이 스레드의 동작 방식이다.
스레드가 두 개일때는 크게 표가 안나지만, 100개 1,000개라면 아주 빠르게 교체되어 진행된다고 해도 진행되는 속도가 전체적으로 느려질 것이다.

(각각의 선이 똑같은 순서나 패턴으로 진행되는 것은 아니고 선마다 속도가 다르다.)

 

ex) 웹사이트 동시접속자 수가 많으면 스레드가 늘어나서 내 일이 처리되는 속도가 느려지는 것.
(작업 단위는 여러개지만 하나의 프로세스상에서 돌아가기 때문에 하나가 진행되는 동안은 다른 스레드가 진행되지 못한다)

멀티프로세스는 실제로 2개의 손으로 같이 그리는것이다.
동시에 진행되는 것!

 


* 빨간 스레드와 파란 스레드가 동일한 객체의 자원에 관여할 경우
- 동일한 객체의 메소드나 필드 사용시 동작이 멈추거나 오류가 날 수 있다.
 빨간 선에서 메소드를 사용중인데, 진행되다가 파란 선이 똑같은 메소드를 호출해서 사용한다면 갑자기 동작이 얼어버릴 것이다.

객체의 필드 역시 빨간 선에서 사용하던 중에 파란 선이 들어와서 필드값을 바꿔버리는 일이 일어날 수도 있다.

- 이런 사고 발생을 막기 위해서는? 사용중에는 다른 선이 들어오지 못하도록 잠가버리면 된다.
 synchronized 라는 예약어를 사용해서 이런 상황을 방지할 수 있다.(근데 그럴일은 별로 없다..)

 


 

CountRunnable 클래스 생성

- Runnable 인터페이스를 구현해서 스레드를 만든다.

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

	@Override
	public void run() {		
		//카운트 값을 저장할 지역변수를 만들고 초기값 대입
		int count=10;
		while(true) {
			System.out.println("현재 카운트:"+count);
			if(count==0) {//만일 count가 0이면
				break; //반복문 탈출(반복문 탈출하면 run() 메소드가 리턴되기 때문에 스레드가 종료된다.
			}
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {			
				e.printStackTrace();
			}
			count--;
		}
	}
}

 

 

패키지04 MyFrame

package frame04;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;

import test.mypac.AnotherThread;
import test.mypac.CountRunnable;

public class MyFrame extends JFrame implements ActionListener{
	
	//생성자
	public MyFrame(String title) {
		super(title);
		//프레임의 초기 설정 작업하기
		setBounds(100,100,500,500);
		//이 프레임의 x버튼(close버튼)을 눌렀을때 프로세스도 같이 종료되도록 설정
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//레이아웃 설정
		setLayout(new FlowLayout());
		
		JButton startBtn=new JButton("카운트 다운 시작");
		startBtn.addActionListener(this);
		//프레임에 버튼 추가		
		add(startBtn);
				
		JTextField tf=new JTextField(10);
		//프레임에 JTextField 추가
		add(tf);
		
		//프레임을 화면상에 실제 보이게 하기(false하면 화면에 보이지않는다.)
		this.setVisible(true);
	}	
	
	//run 했을때 실행의 흐름이 시작되는 특별한 메소드
	public static void main(String[] args) {
		//MyFrame 객체 생성하기
		new MyFrame("나의 프레임");
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		System.out.println("카운트 다운을 시작합니다.");
				
		// Runnable 인터페이스를 구현한 클래스로 객체를 생성해서
		//Runnable r = new CountRunnable();
		// Thread 객체를 생성하면서 인자로 전달
		//Thread t=new Thread(r);
		// Thread 객체의 start() 메소드를 호출해서 새로운 스레드 시작시키기
		//t.start();
		
		//위의 3줄을 한 줄로 작성하면
		new Thread(new CountRunnable()).start();	
	}
}

 

[ 새로운 스레드 만드는 방법2 ] 
  
1. Runnable 인터페이스를 구현한 클래스를 정의한다.
2. run() 메소드를 강제 override 한다.
3. Thread 클래스로 객체를 생성하면서 해당 클래스로 만든 객체를 생성자의 인자로 전달한다.
4. Thread 클래스로 만든 객체의 start() 메소드를 호출해서 스레드를 시작시킨다.

 

- Runnable 인터페이스를 구현해서 override한 상태

- Runnable에는 public void run(); 이라는 추상메소드만 딱 하나 있는 상태이다.

 

- 아까는 스레드를 상속받았기 때문에 스레드 클래스의 모든 기능이 다 들어있었다.

 하지만 이 인터페이스는 추상메소드를 제외하고는 비어 있다. start를 가지고 있지도 않다.

 

- 이렇게는 사용할 수 없다. start라는 메소드가 없기 때문에...

 

 

- 객체를 생성하고 생성자의 인자를 넣어주는 방식으로 가야한다.
new Thread(new CountRunnable()).start();

 

- thread를 상속하는것보다는 조금 불편하지만 runnable인터페이스도 많이 사용한다.

 

- Thread의 많은 생성자 중 2번째를 사용하고 있는 것! Runnable 타입의 인자가 들어간다.

 

 

Runnable r = new CountRunnable();
Thread t=new Thread(r);
t.start();
//
new Thread(new CountRunnable()).start();

- 두개는 같은 코드이다! 위의 세줄을 한줄로 만든 것.

 


 

* 이너클래스로 → 익명클래스로 → 추상메소드가 한개니까 람다식()->{} 으로 수정해볼 예정!

 

 

그런데 굳이 AnotherThread, CountThread, CountRunnable 등의 클래스를 따로 만들어야할까?

→ MyFrame의 이너클래스로 만들면 된다.

 

- 지금 같은 경우에는 MyFrame객체에서 어떤 값을 CountRunnable에 인자로 전달하려고 한다면 어렵다.

- 내부클래스로 만들면 외부클래스의 자원들을 마치 자기것처럼 손쉽게 사용할 수가 있다.

 이너클래스는 클래스를 따로 만든다는 번거로움 이외에도 편리한 점이 많다!

 

- 클래스 안에 또다른 클래스를 정의하면 안쪽클래스에서 바깥클래스의 필드나 메소드를 자유롭게 사용할 수 있다.

- 그래서 인자 등 받아올 값이 있다면 이너클래스로 만드는 게 편리하다.

 

 

패키지05 MyFrame

- 카운트다운 메소드를 가지고있는 클래스를 이너클래스로!

package frame05;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;

import test.mypac.AnotherThread;
import test.mypac.CountRunnable;

public class MyFrame extends JFrame implements ActionListener{
	
	//생성자
	public MyFrame(String title) {
		super(title);
		//프레임의 초기 설정 작업하기
		setBounds(100,100,500,500);
		//이 프레임의 x버튼(close버튼)을 눌렀을때 프로세스도 같이 종료되도록 설정
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//레이아웃 설정
		setLayout(new FlowLayout());
		
		JButton startBtn=new JButton("카운트 다운 시작");
		startBtn.addActionListener(this);
		//프레임에 버튼 추가		
		add(startBtn);
				
		JTextField tf=new JTextField(10);
		//프레임에 JTextField 추가
		add(tf);
		
		//프레임을 화면상에 실제 보이게 하기(false하면 화면에 보이지않는다.)
		this.setVisible(true);
	}	
	
	//run 했을때 실행의 흐름이 시작되는 특별한 메소드
	public static void main(String[] args) {
		//MyFrame 객체 생성하기
		new MyFrame("나의 프레임");
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		System.out.println("카운트 다운을 시작합니다.");		
		//내부 클래스로 객체 생성해서 스레드 시작시키기
		new InnerCountThread().start();		
	}
	
	//MyFrame 클래스의 내부클래스로 스레드 클래스 만들기
	class InnerCountThread extends Thread{
		@Override
		public void run() {		
			//카운트 값을 저장할 지역변수를 만들고 초기값 대입
			int count=10;
			while(true) {
				System.out.println("현재 카운트:"+count);
				if(count==0) {//만일 count가 0이면
					break; //반복문 탈출(반복문 탈출하면 run() 메소드가 리턴되기 때문에 스레드가 종료된다.
				}
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {			
					e.printStackTrace();
				}
				count--;
			}
		}
	}
}

- 스레드를 상속하는 이너클래스 생성

 

- new InnerCountThread().start(); 형태로 호출!

 

- 이너클래스 안에서 바깥 클래스에서 정의된 필드, 메소드 접근이 쉽다.

- 이너클래스 작성후 안에 카운트다운 하는 while문 넣기

 

- 스레드, 이너클래스는 안드로이드 프로그래밍에서 많이사용된다.

 


 

패키지06 MyFrame

- 이너클래스를 익명클래스로! 

 

package frame06;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;

import test.mypac.AnotherThread;
import test.mypac.CountRunnable;

public class MyFrame extends JFrame implements ActionListener{
	
	//생성자
	public MyFrame(String title) {
		super(title);
		//프레임의 초기 설정 작업하기
		setBounds(100,100,500,500);
		//이 프레임의 x버튼(close버튼)을 눌렀을때 프로세스도 같이 종료되도록 설정
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//레이아웃 설정
		setLayout(new FlowLayout());
		
		JButton startBtn=new JButton("카운트 다운 시작");
		startBtn.addActionListener(this);
		//프레임에 버튼 추가		
		add(startBtn);
				
		JTextField tf=new JTextField(10);
		//프레임에 JTextField 추가
		add(tf);
		
		//프레임을 화면상에 실제 보이게 하기(false하면 화면에 보이지않는다.)
		this.setVisible(true);
	}	
	
	//run 했을때 실행의 흐름이 시작되는 특별한 메소드
	public static void main(String[] args) {
		//MyFrame 객체 생성하기
		new MyFrame("나의 프레임");
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		System.out.println("카운트 다운을 시작합니다.");		
		
		new Thread() {
			public void run() {					
				//카운트 값을 저장할 지역변수를 만들고 초기값 대입
				int count=10;
				while(true) {
					System.out.println("현재 카운트:"+count);
					if(count==0) {//만일 count가 0이면
						break; //반복문 탈출(반복문 탈출하면 run() 메소드가 리턴되기 때문에 스레드가 종료된다.
					}
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {			
						e.printStackTrace();
					}
					count--;
				}				
			}
		}.start();
	}	
}

- 클래스를 굳이 만들어야 할까?

 스레드 클래스를 상속받는 익명클래스만들기!

 

 

new Thread(){
	@override
	public void run(){
	}
}.start();

 

- 크게 보면 위와 같은 형태로 만들어진다.

 여기서 @override 표시도 생략하고 메소드를 수정하는 내용만 남긴 것!

 

 

- new Thread는 이름없는 클래스의 생성자를 호출하는 역할이다.

- new Thread() {}.start(); 최종적인 형태. {} 안쪽은 익명클래스이다.

 


 

패키지07 MyFrame

package frame07;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;

import test.mypac.AnotherThread;
import test.mypac.CountRunnable;

public class MyFrame extends JFrame implements ActionListener{
	
	//생성자
	public MyFrame(String title) {
		super(title);
		//프레임의 초기 설정 작업하기
		setBounds(100,100,500,500);
		//이 프레임의 x버튼(close버튼)을 눌렀을때 프로세스도 같이 종료되도록 설정
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//레이아웃 설정
		setLayout(new FlowLayout());
		
		JButton startBtn=new JButton("카운트 다운 시작");
		startBtn.addActionListener(this);
		//프레임에 버튼 추가		
		add(startBtn);
				
		JTextField tf=new JTextField(10);
		//프레임에 JTextField 추가
		add(tf);
		
		//프레임을 화면상에 실제 보이게 하기(false하면 화면에 보이지않는다.)
		this.setVisible(true);
	}	
	
	//run 했을때 실행의 흐름이 시작되는 특별한 메소드
	public static void main(String[] args) {
		//MyFrame 객체 생성하기
		new MyFrame("나의 프레임");
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		System.out.println("카운트 다운을 시작합니다.");		
		
		new Thread(new Runnable() {		
			@Override
			public void run() {
				//카운트 값을 저장할 지역변수를 만들고 초기값 대입
				int count=10;
				while(true) {
					System.out.println("현재 카운트:"+count);
					if(count==0) {//만일 count가 0이면
						break; //반복문 탈출(반복문 탈출하면 run() 메소드가 리턴되기 때문에 스레드가 종료된다.
					}
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {			
						e.printStackTrace();
					}
					count--;
				}
			}
		}).start();
		
	}	
}

- () -> {} 식으로 수정

- Thread를 상속하는 클래스 대신 new Runnable을 사용해 스레드 생성

 

- Runnable에 익명클래스 생성

 ()->{} 으로 수정할 수도 있다. 추상메소드 하나짜리이므로.

 

- Thread의 인자로 new Runnable을 받으면서

  Runnable 에 익명 이너클래스 생성하기

 


 

패키지08 MyFrame

- 람다식 작성

package frame08;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;

import test.mypac.AnotherThread;
import test.mypac.CountRunnable;

public class MyFrame extends JFrame implements ActionListener{
	
	//생성자
	public MyFrame(String title) {
		super(title);
		//프레임의 초기 설정 작업하기
		setBounds(100,100,500,500);
		//이 프레임의 x버튼(close버튼)을 눌렀을때 프로세스도 같이 종료되도록 설정
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//레이아웃 설정
		setLayout(new FlowLayout());
		
		JButton startBtn=new JButton("카운트 다운 시작");
		startBtn.addActionListener(this);
		//프레임에 버튼 추가		
		add(startBtn);
				
		JTextField tf=new JTextField(10);
		//프레임에 JTextField 추가
		add(tf);
		
		//프레임을 화면상에 실제 보이게 하기(false하면 화면에 보이지않는다.)
		this.setVisible(true);
	}	
	
	//run 했을때 실행의 흐름이 시작되는 특별한 메소드
	public static void main(String[] args) {
		//MyFrame 객체 생성하기
		new MyFrame("나의 프레임");
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		System.out.println("카운트 다운을 시작합니다.");		
		
		new Thread(()->{
			//카운트 값을 저장할 지역변수를 만들고 초기값 대입
			int count=10;
			while(true) {
				System.out.println("현재 카운트:"+count);
				if(count==0) {//만일 count가 0이면
					break; //반복문 탈출(반복문 탈출하면 run() 메소드가 리턴되기 때문에 스레드가 종료된다.
				}
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e2) {			
					e2.printStackTrace();
				}
				count--;
			}			
		}).start();		
	}	
}

- new Thread(()->{}).start();

 

 

- 파란 밑줄 부분은 Runnable type이다. 익명클래스로 객체를 생성한 것

  ( ) -> { } : "객체를 -> 메소드로" 전달하는 느낌!

 

- 변수에 잘 들어가지는 것에서 Runnable타입인 것을 확인할 수 있다.