반응형

 

Process와 Thread

 

Process

: 실행 중인 프로그램

 

 

Multi Process

: 여러 개의 Thread를 실행하여 작동하는 프로그램

ex) 채팅

 

 

Multi Tasking

: 두 개 이상의 프로세스를 실행하여 일을 처리하는 것.

 

 

Thread

: 프로세스(process) 내에서 실제로 작업을 수행하는 주체

 

스레드는 비가역적이므로 종료한 스레드를 다시 불러와 실행할 수 없다. 똑같은 작업을 하고 싶을 시 새로운 스레드를 만들어 작동시켜야 한다.

 

 

싱글 스레드 프로그램 예시

 

 

package kr.or.ddit.basic;

public class T01ThreadTest {
	public static void main(String[] args) {
		// 싱글 스레드 프로그램
		for(int i=1; i<=200; i++) {
			System.out.print("*");
		}
		
		System.out.println();
		
		for (int i = 0; i <= 200; i++) {
			System.out.print("$");
		}
	}
}

 

결과 화면1

 

 

 

멀티 스레드 프로그램 예시

 

스레드 구조적 작동 방법

 

 

- 스레드 생성 방법
1. Thread클래스를 상속한 클래스의 인스턴스를 생성 > 해당 인스턴스의 start()메서드 호출.
2. Runnable인터페이스를 구현한 클래스의 인스턴스를 생성 > Thread객체의 인스턴스 생성 시 생성자의 파라미터값으로 넣어줌 > 생성된 Thread객체의 start() 메서드 호출.
3. 익명클래스를 이용하는 방법
  Runnable인터페이스를 구현한 익명 클래스를 Thread객체 생성 시 생성자의 파라미터값으로 넣어줌 > 생성된 Thread객체의 start()메서드 호출.

 

 

 

  • 방법 1 이용

두 개의 스레드를 사용

Thread 메소드에서 작동할 것들은 run() 에 넣어놔야함

package kr.or.ddit.basic;

public class T02ThreadTest {
	public static void main(String[] args) {
		// 멀티 스레드 프로그램
		
		// 스레드 생성하기
		
		// 방법1 : Thread클래스를 상속한 클래스의 인스턴스를 생성한 후 해당 인스턴스의 start()메서드를 호출한다.
		Thread th1 = new MyThread1();
		th1.start();
	}
}

class MyThread1 extends Thread {
	
	@Override
	public void run() {
		for(int i=1; i<=200; i++) {
			System.out.print("*");
			
			try {
				// Thread.sleep(시간) => 주어진 시간동안 작업을 잠시 멈춤.
				// 시간은 밀리세컨드 단위 사용 (1000 밀리세컨드 = 1초).
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

 

결과 화면2 : 100 밀리세컨드 시간 간격으로 * 을 찍음

 

 

 

  • 방법 2 이용
package kr.or.ddit.basic;

public class T02ThreadTest {
	public static void main(String[] args) {
		// 멀티 스레드 프로그램
		
		// 스레드 생성하기
		
		// 방법1 : Thread클래스를 상속한 클래스의 인스턴스를 생성한 후 해당 인스턴스의 start()메서드를 호출한다.
		Thread th1 = new MyThread1();
		th1.start();
		
		// 방법2 : Runnable인터페이스를 구현한 클래스의 인스턴스를 생성한 후 이 인스턴스를 Thread객체의 인스턴스를
		//		  생성할 때 생성자의 파라미터값으로 넣어준다. 이때 생성된 Thread객체의 start() 메서드를 호출한다.
		Runnable r = new MyThread2();
		Thread th2 = new Thread(r);
		th2.start();
	}
}

class MyThread1 extends Thread {
	
	@Override
	public void run() {
		for(int i=1; i<=200; i++) {
			System.out.print("*");
			
			try {
				// Thread.sleep(시간) => 주어진 시간동안 작업을 잠시 멈춤.
				// 시간은 밀리세컨드 단위 사용 (1000 밀리세컨드 = 1초).
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

class MyThread2 implements Runnable {
	@Override
	public void run() {
		for(int i=1; i<=200; i++) {
			System.out.print("$");
			
			try {
				// Thread.sleep(시간) => 주어진 시간동안 작업을 잠시 멈춤.
				// 시간은 밀리세컨드 단위 사용 (1000 밀리세컨드 = 1초).
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

 

결과 화면3

 

 

 

  • 방법 3 이용
package kr.or.ddit.basic;

public class T02ThreadTest {
	public static void main(String[] args) {
		// 멀티 스레드 프로그램
		
		// 스레드 생성하기
		
		// 방법1 : Thread클래스를 상속한 클래스의 인스턴스를 생성한 후 해당 인스턴스의 start()메서드를 호출한다.
		Thread th1 = new MyThread1();
		th1.start();
		
		// 방법2 : Runnable인터페이스를 구현한 클래스의 인스턴스를 생성한 후 이 인스턴스를 Thread객체의 인스턴스를
		//		  생성할 때 생성자의 파라미터값으로 넣어준다. 이때 생성된 Thread객체의 start() 메서드를 호출한다.
		Runnable r = new MyThread2();
		Thread th2 = new Thread(r);
		th2.start();
		
		// 방법3 : 익명클래스를 이용하는 방법
		//	  Runnable인터페이스를 구현한 익명 클래스를 Thread객체 생성 시 생성자의 파라미터값으로 넣어준다.
		//	  이 때 생성된 Thread객체의 start()메서드를 호출한다.
		Thread th3 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				for(int i=1; i<=200; i++) {
					System.out.print("@");
					
					try {
						// Thread.sleep(시간) => 주어진 시간동안 작업을 잠시 멈춤.
						// 시간은 밀리세컨드 단위 사용 (1000 밀리세컨드 = 1초).
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});
		th3.start();
	}
}

class MyThread1 extends Thread {
	
	@Override
	public void run() {
		for(int i=1; i<=200; i++) {
			System.out.print("*");
			
			try {
				// Thread.sleep(시간) => 주어진 시간동안 작업을 잠시 멈춤.
				// 시간은 밀리세컨드 단위 사용 (1000 밀리세컨드 = 1초).
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

class MyThread2 implements Runnable {
	@Override
	public void run() {
		for(int i=1; i<=200; i++) {
			System.out.print("$");
			
			try {
				// Thread.sleep(시간) => 주어진 시간동안 작업을 잠시 멈춤.
				// 시간은 밀리세컨드 단위 사용 (1000 밀리세컨드 = 1초).
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

 

결과 화면4 : 메인 스레드 포함하여 총 4개의 스레드로 구정된 프로그램

 

 

- 방법 2와 방법 3의 차이

public class Test {
	public static void main(String[] args) {
		// extends Thread 를 사용할 때
		스레드이름1 th1 = new 스레드이름1();

		// implements Runnable 를 사용할 때
		Thread th2 = new Thread(new 스레드이름2());
}

class 스레드이름1 extends Thread {
	public 스레드이름1() {
	}
}

class 스레드이름2 implements Runnable {
	public 스레드이름2() {
	}
}

 

 

 

  • 스레드 사용법

extends Thread 또는 implements Runnable 하여 작동하여야 한다.

사용법
start() 스레드 작업 시작
run() 스레드 작업 진행 (main 메소드와 같은 기능으로 생각하면 됨)
Thread.sleep() 특정 시간동안 작업을 잠시 멈춤
join() 작업중인 스레드가 종료될 때까지 기다림
System.exit(0); 프로그램 종료

 

 

 

  • 스레드 예시

 

- System.currentTimeMillis() 이용 => 처리시간

package kr.or.ddit.basic;

public class T03ThreadTest {
	public static void main(String[] args) {
		// 스레드의 수행시간 체크하기
		Thread th = new Thread(new MyRunner());
		
		// UTC(Universal Time Coordinated 협정 세계 표준 시)를 사용하여
		// 1970년 1월 1일 0시 0분 0초를 기준으로 경과한 시간을 밀리세컨드(1/1000초) 단위로 나타낸다.
		long startTime = System.currentTimeMillis();
		
		th.start(); // 스레드 작업 시작
		
		try {
			th.join();
			// 현재 실행중인 스레드에서 작업중인 스레드(지금은 th스레드)가 종료될 때까지 기다린다.
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		long endTime = System.currentTimeMillis();
		
		System.out.println("경과 시간 : " + (endTime - startTime) + " ms");
	}
}

// 1 ~ 1000000000 까지의 합계를 구하기 위한 클래스
class MyRunner implements Runnable {
	@Override
	public void run() {
		long sum = 0;
		for (int i = 0; i <= 1000000000; i++) {
			sum += i;
		}
		System.out.println("합계 : " + sum);
	}
}

 

결과 화면5

 

 

=> 단일 스레드보다 멀티 스레드로 할 시 더 빠르게 작업할 수 있음

package kr.or.ddit.basic;

public class T04ThreadTest {
	/*
	 * 1 ~ 20억까지의 합계를 구하는 데 걸린 시간을 확인해보기
	 * 
	 * 전체 합계를 구하는 작업을 단일 스레드로 처리했을 때와 여러개의 스레드로 분할해서
	 * 작업할 때의 시간을 확인해보자.
	 */
	
	public static void main(String[] args) {
		// 단일 스레드로 처리할 때...
		SumThread sm = new SumThread(1, 2000000000);
		long startTime = System.currentTimeMillis();
		sm.start();
		try {
			sm.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		long endTime = System.currentTimeMillis();
		
		System.out.println("단독으로 처리 할 때의 처리시간(ms) : " + (endTime - startTime));
		System.out.println("\n\n");
		
		// 여러 스레드가 나누어 작업할 때
		SumThread[] sumThs = new SumThread[] {
				new SumThread(			1L,  500000000L),
				new SumThread(	500000000L, 1000000000L),
				new SumThread( 1000000000L, 1500000000L),
				new SumThread( 1500000000L, 2000000000L)
		};
		
		startTime = System.currentTimeMillis();
		
		for (Thread th : sumThs) {
			th.start();
		}
		
		for (Thread th : sumThs) {
			try {
				th.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		endTime = System.currentTimeMillis();
		System.out.println("여러 스레드로 나누어 처리 했을 때의 처리시간(ms) : "
				+ (endTime - startTime));
	}
}

class SumThread extends Thread {
	private long min, max;
	
	public SumThread(long min, long max) {
		this.min = min;
		this.max = max;
	}
	
	@Override
	public void run() {
		long sum = 0;
		for (long i = min; i < max; i++) {
			sum += i;
		}
		System.out.println(min + " ~ " + max + " 까지의 합 : " + sum);
	}
}

 

결과 화면6

 

 

- JOptionPane.showInputDialog 이용 => 입력 받기

package kr.or.ddit.basic;

import javax.swing.JOptionPane;

/**
 * 단일스레드에서 사용자 입력 처리
 */
public class T05ThreadTest {
	public static void main(String[] args) {
		String input = JOptionPane.showInputDialog("아무거나 입력하세요.");
		
		System.out.println("입력한 값은 " + input + "입니다.");
		
		for(int i=10; i>=1; i--) {
			System.out.println(i);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

 

실행 시 화면이 뜸

 

결과 화면7 : 값 입력 후 첫줄 출력 후 10부터 1초 간격으로 1까지 출력됨

 

 

package kr.or.ddit.basic;

import javax.swing.JOptionPane;

public class T06ThreadTest {
	public static void main(String[] args) {
		Thread th1 = new DataInput();
		Thread th2 = new CountDown();
		
		th1.start(); 
		th2.start(); 
	}
}

/**
 * 사용자 입력을 받기 위한 스레드
 */
class DataInput extends Thread {
	@Override
	public void run() {
		String input = JOptionPane.showInputDialog("아무거나 입력하세요.");
		
		System.out.println("입력한 값은 " + input + "입니다.");
	}
}

/**
 * 카운트다운을 처리하기 위한 스레드
 */
class CountDown extends Thread {
	@Override
	public void run() {
		for(int i=10; i>=1; i--) {
			System.out.println(i);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

 

결과 화면8-1 : 동시에 작동되며 값을 입력하지 않을 시 프로그램이 종료되지 않음

 

결과 화면8-2 : 값 입력시, main스레드 > CountDown스레드 > DataInput 스레드 순으로 종료됨

 

 

package kr.or.ddit.basic;

import javax.swing.JOptionPane;

public class T06ThreadTest {
	public static boolean inputCheck = false;
	
	public static void main(String[] args) {
		Thread th1 = new DataInput();
		Thread th2 = new CountDown();
		
		th1.start(); 
		th2.start(); 
	}
}

/**
 * 사용자 입력을 받기 위한 스레드
 */
class DataInput extends Thread {
	@Override
	public void run() {
		String input = JOptionPane.showInputDialog("아무거나 입력하세요.");
		
		T06ThreadTest.inputCheck = true;
		
		System.out.println("입력한 값은 " + input + "입니다.");
	}
}

/**
 * 카운트다운을 처리하기 위한 스레드
 */
class CountDown extends Thread {
	@Override
	public void run() {
		for(int i=10; i>=1; i--) {
			
			// 입력이 완료되었는지 여부를 확인하고 입력이 완료된 경우에는
			// run()메서드를 종료한다.
			if(T06ThreadTest.inputCheck) {
				return; // run()메서드가 종료되면 해당 스레드도 종료된다.
			}
			
			System.out.println(i);
			
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		// 10초가 경과되었는데도 입력이 없으면 프로그램을 종료한다.
		System.out.println("10초가 지났습니다. 프로그램을 종료합니다.");
		System.exit(0); // 프로그램 종료시키는 명령어
	}
}

 

결과 화면9-1 : 카운트 다운 실행 중에 입력 시 모든 스레드가 종료됨

 

결과 화면9-2 : 카운트 다운 시간이 전부 지나면 자동적으로 종료됨

 

 

 

- setPriority() 이용 => 우선 순위 변경

package kr.or.ddit.basic;

public class T08ThreadPriorityTest {
	public static void main(String[] args) {
		System.out.println("최대 우선순위 : " + Thread.MAX_PRIORITY);
		System.out.println("최소 우선순위 : " + Thread.MIN_PRIORITY);
		System.out.println("보통 우선순위 : " + Thread.NORM_PRIORITY);
		
		Thread[] ths = new Thread[] {
				new ThreadTest1(),
				new ThreadTest1(),
				new ThreadTest1(),
				new ThreadTest1(),
				new ThreadTest1(),
				new ThreadTest2()
		};
		
		// 우선 순위는 start()메서드를 호출하기 전에 설정해야 한다.
		for(int i=0; i<ths.length; i++) {
			if(i == 5) {
				ths[i].setPriority(10);
			} else {
				ths[i].setPriority(1);
			}
		}
		
		// 우선 순위 정보 출력하기
		for (Thread th : ths) {
			System.out.println(th.getName() + "의 우선 순위 : " + th.getPriority());
		}
		
		// 스레드 구동하기
		for (Thread th : ths) {
			th.start();
		}
	}
}

// 대문자를 출력하는 스레드
class ThreadTest1 extends Thread {
	@Override
	public void run() {
		for(char ch='A'; ch<='Z'; ch++) {
			System.out.println(ch);
			
			// 아무것도 하지 않는 반복문(시간 때우기용)
			for(long i=1; i<=1000000000L; i++) {}
		}
	}
}

// 소문자를 출력하는 스레드
class ThreadTest2 extends Thread {
	@Override
	public void run() {
		for(char ch='a'; ch<='z'; ch++) {
			System.out.println(ch);
			
			// 아무것도 하지 않는 반복문(시간 때우기용)
			for(long i=1; i<=1000000000L; i++) {}
		}
	}
}

 

결과 화면10 : 실행된 순서부터 차례대로 우선순위에 맞춰 출력됨 but 100% 우선 순위에 맞춰 실행되지 않을 수 있음

 

 

 

  • save() & setDaemon 이용 => 자동 저장 및 종료

 

Daemon Thread

: 다른 일반 스레드의 작업을 돕는 보조적인 스레드. 일반 스레드가 모두 종료되면 자동으로 종료된다.

start 메서드 호출 전에 설정해야 한다.

 

// start() 작동 전에 선언 필요!
스레드객체.setDaemon(true);
스레드객체.start();

 

 

package kr.or.ddit.basic;

public class T09ThreadDaemonTest {
	public static void main(String[] args) {
		AutoSaveThread autoSave = new AutoSaveThread();
		
		// 데몬스레드 설정은 start()메서드 호출 전에 해주어야 한다.
		autoSave.setDaemon(true);
		autoSave.start();
		
		for(int i=1; i<=20; i++) {
			System.out.println("작업 " + i);
			
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		System.out.println("메인 스레드 종료...");
	}
}

/**
 * 자동 저장기능을 제공하는 스레드
 */
class AutoSaveThread extends Thread {
	public void save() {
		System.out.println("작업 내용을 저장합니다.");
	}
	
	@Override
	public void run() {
		while (true) {
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			save(); // 저장기능 호출
		}
	}
}

 

결과 화면11-1

 

결과 화면11-2 : 속성(setDaemon) 주기 전에는 계속해서 작동함

 

 

반응형