Thread 그룹은 프로젝트가 커질수록 그에 맞춰 쓰레드도 많아지게 된다.


몰론 스레드마다 이름을 지정해 관리 할 수 있지만 숫자가 많아지면 한계가 있다.


그러한 이유로 그룹으로 묶어 공통으로 설정하고 제어 할 수 있는 기능이 바로 Thread Group이다


기본적으로 모든 스레드는 Main Group으로 자동적으로 설정이 되고





- 스레드 그룹 이름 얻기 -


Threadgroup group = Thread.currentThread.getThreadGroup();


String groupName = group.getName();



스레드 그룹을 생성 시 부모 그룹과 묶을 수 있는데 만약 묶지 않는다면 현재 스레드의 하위 그룹과 묶인다.


- 스레드 그룹 생성 -


Threadgroup group = new ThreadGroup(String name);


Threadgroup group = new ThreadGroup(ThreadGroup parent, String name);




- 스레드에 생성 시 그룹 부여 방법 4가지 -


Thread th = new Thread(Thread group, Runnable target);


Thread th = new Thread(Thread group, String name);


Thread th = new Thread(Thread group, Runnable target, String name);


Thread th = new Thread(Thread group, Runnable target, String name, long stackSize);




그룹을 통해 묶은 스레드는 메소드로 한번에 통제 할 수 있다.


예를들어 interrupt()로 한꺼번에 정지 시키거나


setDaemon(boolean)으로 데몬 그룹으로 설정 할 수 있다.


다양한 메소드는 직접 Eclipse에서 ThreadGroup이름에서 '.'을 눌러 나오는 자동완성 메소드들을 통해서 확인해보면


익숙한 메소드가 많을 것이다.














'개념 및 코딩 > 08.Thread 스레드' 카테고리의 다른 글

[JAVA]08-07.Thread Pool, Pool Block  (0) 2018.09.06
[JAVA]08-05.Demon Thread  (0) 2018.09.06
[JAVA]08-04.Thread State, Control  (0) 2018.09.06
[JAVA]08-03.Priority, Synchronized  (0) 2018.09.06
[JAVA]08-02.Runnable, Thread  (0) 2018.09.04

Thread Pool은 다수의 스레드 자원 할당을 좀 더 효율적으로 하는 작업이다.


Thread pool을 생성 할 때 스레드 수를 지정하고


Runnable 작업이나 Callable 작업을 받고 실행된다.


단순하게 thread.start를 한다면 os의 작업 스케줄링으로 작업이 진행되는데


pool은 작업 할 Thread 수를 미리 설정해서 스케줄링이 진행된다.


하지만 Thread 수를 잘 못 예측하여 높게 잡는다면 오히려 메모리 공간이 낭비된다.




Runnable 작업은 Return값이 없고


Callable 작업은 Return값이 있는게 특징이다.


생성된 작업은 submit(), execute()로 실행되고


execute는 반환값이 없고 예외가 발생하면 스레드가 제거되는 반면


submit은 Future타입을 반환하고 예외상황에도 스레드를 재사용한다.




Thread Pool은 main Thread가 종료되도 남아있기 때문에 상태를 확인하고 따로 종료를 해주어야 한다.


shutdown()은 현재 작업을 마무리 후 종료시키고 shutdownNow()는 interrupt를 일으켜 강제 종료시킨다.



package chap10.exam13.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPool {

	public static void main(String[] args) {
		// ThreadPool 생성 방법
		// 1개 이상의 스레드가 추가 되었을 때 60초 이상 놀고 있으면 제거
		// 작은 캐쉬 메모리 방에서 오래 붙들고 있으면 낭비가 심하기 때문에 제거
		ExecutorService pool = Executors.newCachedThreadPool();
		
		// 실행환경, 현재 이용할 수 있는 스레드, 프로세서 불러오는 방법
		// 현재 CPU에서 코어 갯수를 추출하는 방법.
		int n = Runtime.getRuntime().availableProcessors(); 
		System.out.println("가용 할 수 있는 스레드 갯수 : " + n);
		// 사용할 스레드, 프로세서 갯수를 지정하는법
		// 기본적으로 n개의 스레드를 유지
		pool = Executors.newFixedThreadPool(n);
	}

}







package chap10.exam14.exec;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ExecuteMain {

	public static void main(String[] args) throws InterruptedException {
		// Runnable로 작업 요청
		// execute() : 용서 X, 반환 X // 혼자 실행
		// submit() : 용서 O, 반환 O // 전송에는 답변이 있어야 함
		
		// 스레드 순서가 중요
		// 1. 스레드 풀 생성
		int n = Runtime.getRuntime().availableProcessors();
		ExecutorService pool = Executors.newFixedThreadPool(n);
		// 2. 할 일 지정
		Runnable runnable = new Runnable() {
			
			@Override
			public void run() {
				System.out.println("Runnable 처리");
			}
		};
		// 3. 실행
		pool.execute(runnable); // 반환 값 X
		
		
		// 4. 스레드 풀 종료(close)
		// 처리중인 작업이 마무리 되면 닫는다.
		pool.shutdown();
		// 처리중인 작업에 관계없이 강제로 닫는다.
//		pool.shutdownNow();
		// shutdown()이 타임아웃안에 실행 되는지 반환
		// 제한 시간안에 작업 완료시 true를 반환
		// 만약 제한시간을 지날경우 강제로 닫음
//		boolean t = pool.awaitTermination(10L, TimeUnit.SECONDS);
		
		
	}

}


package chap10.exam14.exec;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Submit {

	public static void main(String[] args) throws Exception {
		// 1. 스레드 풀 생성
		ExecutorService pool = Executors.newCachedThreadPool();
		
		// 2. 할 일 만들기
		Callable<String> callable = new Callable<String>() {

			@Override
			public String call() throws Exception {
				System.out.println("Callable 처리");
				return "작업완료";
			}
		};
		// 3. 실행
		Future<String> submit = pool.submit(callable);
		System.out.println(submit.get());
		
		// 4. 스레드 풀 닫기
		pool.shutdown();
		
	}

}


package chap10.exam14.exec;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestPool {

	public static void main(String[] args) throws Exception {
		// Thread Pool 10번 출력하기

		// Runnable 사용, 순차실행
		// 1. 스레드 풀 생성
		int n = Runtime.getRuntime().availableProcessors();
		ExecutorService pool = Executors.newFixedThreadPool(n);
		// 2. 할 일 지정
		Runnable runnable = new Runnable() {

			@Override
			public void run() {
				for(int i = 0; i < 10; i++) {
					System.out.println("Thread Pool " + (i + 1) + "번");
				}
				System.out.println("----------------------------");
			}
		};
		// 3. 실행
		pool.execute(runnable); // 반환 값 X

		// 4. 스레드 풀 종료(close)
		pool.shutdown();





		// Callable 사용, 자기 마음대로 실행
		// 1. 스레드 풀 생성
		ExecutorService pool2 = Executors.newCachedThreadPool();

		// 2. 할 일 만들기
		Callable<String> callable = new Callable<String>() {

			@Override
			public String call() throws Exception {

				System.out.println("Thread Pool1");

				return "작업완료";
			}
		};

		// 3. 실행
		Future<String> submit = null;
		for(int i =0; i < 10; i++) {
			System.out.print(i + " : ");
			submit = pool2.submit(callable);
		}
		System.out.println(submit.get());

		// 4. 스레드 풀 닫기
		pool2.shutdown();

	}

}



Thread Pool은 runnable이나 callable에 관계없이 작업이 완료되면 Future객체를 반환한다.


그 Future 객체를 가져오는 get 메소드는 join과 같은 역할을 수행하고


이러한 작업을 Pool Blocking이라고 한다.



package chap10.exam15.block;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class NoResultMain {

	public static void main(String[] args) throws Exception {

		int n = Runtime.getRuntime().availableProcessors();
		ExecutorService pool = Executors.newFixedThreadPool(n);

		Runnable runnable = new Runnable() {

			@Override
			public void run() {
				for(int i = 0; i < 10; i++) {
					System.out.println("Thread Pool " + (i + 1) + "번");
				}
				System.out.println("----------------------------");
			}
		};
		
		Future future = pool.submit(runnable); // 반환할게 따로 없으므로 제너릭 지워줌
		// 반환값이 없는데 get() 사용 이유
		future.get(); // get() == join() / 이 아래 내용이 반환 후 실행
		System.out.println("작업 완료");
		pool.shutdown();
	}

}


package chap10.exam15.block;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ResultMain {

	public static void main(String[] args) throws Exception {
		// 1. 풀 생성
		int n = Runtime.getRuntime().availableProcessors();
		ExecutorService pool = Executors.newFixedThreadPool(n);
		// 2. 해야 할 일
		Callable<Integer> task = new Callable<Integer>() {

			@Override
			public Integer call() throws Exception {
				int sum = 0;
				for(int i = 1; i <= 100; i++) {
					sum += i;
				}
				return sum;
			}
		};
		// 3. 실행
		Future<Integer> future = pool.submit(task);
		int result = future.get();	
		System.out.println("합 : " + result);
		// 4. 풀 닫기
		pool.shutdown();
	}

}





1.데몬 스레드, Demon Thread


Main이 종료 될 때 직접 작성한 스레드들은 따로 끝내지 않으면 계속 돌아가게 되는데


Thread가 많아 질 수록 리소스를 많이 가져가고


꺼지지 않은 Thread가 만약 공유하는 변수값을 수정한다면 프로그램 자체가 꼬이게 된다.


그래서 스레드 start 전에 setDaemon(true) 설정을 하면


스레드를 실행한 메인 스레드가 종료된다면 강제로 같이 종료가 된다.


다음 예를 보자


package chap10.exam12.demon;

public class DemonThread extends Thread {

	@Override
	public void run() {		
		while(true) {
			System.out.println("Demon Thread Running...");
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
}
<


package chap10.exam12.demon;

public class MainThread {

	public static void main(String[] args) throws InterruptedException {
		Thread demon = new DemonThread();
		demon.setDaemon(true);//work -> demon
		demon.start();//데몬 스레드 시작
		Thread.sleep(3000);//3초후
		System.out.println("메인 스레드 종료");
	}

}


1.State


State는 Thread의 작업을 감시하는 역할을 한다.


스레드의 상태가 어떤지 지속적으로 알려주는 기능이다.


활용도는 낮은편..



package chap10.exam06.state;

public class WorkThread extends Thread {

	@Override
	public void run() {

		for(long i=0; i<1000;i++) {
			System.out.println(i);
		}

		try {
			Thread.sleep(1500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		for(long i=0; i<1000;i++) {
			System.out.println(i);
		}

	}
	
}
package chap10.exam06.state;

public class Main {
	
	private static Thread.State state;

	public static void main(String[] args) throws InterruptedException {
		/*워크 스레드 : 생성 -> 실행 -> 일시정지 -> 실행 -> 종료*/
		/*메인스레드 : 워크스레드를 감시*/
		WorkThread work = new WorkThread();

		//메인 스레드는 지속적으로 워크 스레드를 감시
		while(true) {
			state = work.getState();
			System.out.println("THREAD STATE : "+state);
			//객체 생성시 워크 스레드 실행
			if(state == Thread.State.NEW) {
				work.start();
			}			
			/*Runnable 이 순식간에 진행 되므로 잘 잡히지 않는다.
			Sleep 을 제거 하면 볼수는 있다.
			*/			
			Thread.sleep(500); //0.5초 휴식
			
			if(state == Thread.State.TERMINATED) {
				break;
			}
			
		}

	}

}





2.Control(sleep, yield, join, wait, notify, stop, interrupt)


2-1 sleep, yield


sleep은 Thread를 대기 시키는 메소드로 안에 인자를 받는데 mil second로 1000값을 주면 1초동안 Thread를 대기 시킨다.


yield는 작업을 셀프 인터럽트 시키는 메소드로 작업을 다음 순서 스레드에게 양보한다고 이해하면 된다.



package chap10.exam07.yield;

public class ThreadA extends Thread {
	
	boolean stop = false;
	boolean yield = false;

	@Override
	public void run() {
		
		while(!stop) {
			System.out.println("Thread A 동작");
			if(yield) {
				System.out.println("Thread B 에게 양보");
				Thread.yield();
			}			
			
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("Thread A 중지");

	}
	
	

}
package chap10.exam07.yield;

public class ThreadB extends Thread {
	
	boolean stop = false;
	boolean yield = false;

	@Override
	public void run() {
		
		while(!stop) {
			System.out.println("Thread B 동작");
			if(yield) {
				System.out.println("Thread A 에게 양보");
				Thread.yield();
			}			
			
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("Thread B 중지");

	}

}
package chap10.exam07.yield;

public class MainThread {

	public static void main(String[] args) throws InterruptedException {
		/*
			yield 는 자신이 쉬겠다는 의미가 아님
			다른 스레드에게 할 수 있는 기회를 양보 하는 것
			실행 순서 정도에 변화를 줄 수 있다.
		*/

		//A,B 스레드 생성 후 실행
		ThreadA thA = new ThreadA();
		ThreadB thB = new ThreadB();
		thA.start();
		thB.start();		
		
		//A 에게 양보 하라고 함(B 에게 우선권 양보)
		thA.yield = true;
		Thread.sleep(1000);
		//B 에게 양보 하라고 함(A 에게 우선권 양보)
		thB.yield = true;
		Thread.sleep(1000);		
		
		//A,B 정지
		thA.stop = true;
		thB.stop = true;	
		
	}

}



2-2. join


스레드가 병렬로 처리 될 경우 작업 자체가 분리되는 부분이 있는데 join을 사용하면 작업이 공통 처리된다.


다음 예제는 메인 스레드와 oper 스레드가 join을 사용했을 때와


주석처리하여 사용하지 않았을 때의 결과를 비교하는 예제이다.


한번 직접 oper.join(); 을 주석처리하여 테스트 해보자.



package chap10.exam08.join;

public class OperThread extends Thread{

	private int sum = 0;
	
	@Override
	public void run() {		
		for(int i=1; i<=100; i++) {
			sum += i;
		}
	}

	public int getSum() {
		return sum;
	}

}
package chap10.exam08.join;

public class MainThread {

	public static void main(String[] args) throws InterruptedException {
		//1~100 까지의 합
		OperThread oper = new OperThread();
		oper.start();//oper : 실제 계산 수행
		
		oper.join();//oper 와 같이 가라, 이 줄을 주석처리 하여 테스트
		//oper 의 수행 내용이 끝나고 나면 아래가 실행 된다.
		//main : 계산 내용을 출력
		System.out.println("1~100 의 합 : "+oper.getSum());
	}

}




2-3. wait, notify


wait은 스레드를 대기상태로 만들고


notify는 스레드 중 하나를 실행대기 상태로 만들고


notifyall은 스레드 전체를 실행대기 상태로 만든다.


대기상태는 작업을 일시정지 하는것이고 실행대기는 작업 대기열에 넣어 연산을 준비하는 것이다.



package chap10.exam10.test;

public class CommonData {
	
	private String data = null;
	//wait(), notify() 등을 호출하기 위해서는 synchronized 필요
	public synchronized void getData() {
		try {			
			if(data != null) {//1. 데이터가 있으면 가져온다.
				System.out.println("가져온 데이터 : "+this.data);
				this.data = null;//가져왔으면 비워주고
			}			
			notify();	//2. 깨우고
			wait();		//3. 난 쉰다.
		} catch (InterruptedException e) {
			e.printStackTrace();
		}	
	}

	public synchronized void setData(String data) {		
		try {
			//1. 데이터가 없으면 만든다.
			if(this.data == null) {
				System.out.println("넣을 데이터 : "+data);
				this.data = data;
			}			
			notify();	//2. 넣고 나면 깨우고
			wait();		//3. 난 쉰다.
		} catch (InterruptedException e) {
			e.printStackTrace();
		}		
	}	

}
package chap10.exam10.test;

public class Consumer extends Thread{
	
	private CommonData data;

	public Consumer(CommonData data) {
		this.data = data;
	}

	@Override
	public void run() {
		for(int i=1; i<=5;i++) {
			data.getData();
		}
	}	

}
package chap10.exam10.test;

public class Producer extends Thread{
	
	private CommonData data;

	public Producer(CommonData data) {
		this.data = data;
	}

	@Override
	public void run() {
		for(int i=1; i<=5;i++) {
			data.setData("DATA_"+i);
		}
	}	

}
package chap10.exam10.test;

public class Main {

	public static void main(String[] args) throws InterruptedException {
		
		CommonData data = new CommonData();
		
		Producer pro = new Producer(data);
		Consumer con = new Consumer(data);
		pro.start();
		Thread.sleep(10);
		con.start();

	}

}






2-4. stop, interrupt


stop과 interrupt는 Thread 작성 시 run에서 작업중인 내용일 종료시켜버린다.


Thread는 보통 while(true)를 통해서 무한루프를 돌려 감시체계를 만든다던가 특정 작업을 받기위해 대기하고 있다던가


그러한 경우로 자주 쓰이기 때문에 강제 종료해주는 기능이 필요할 때가 있다.


이때 stop() 을 사용 할 수 있으나 현재는 사용중지를 권고 하고 있다.



package chap10.exam11.stop;

public class Inter extends Thread {

	@Override
	public void run() {
		/*
		try {
			while(true) {
				System.out.println("Inter 실행 중");
				Thread.sleep(1);
			}
		} catch (InterruptedException e) {
			System.out.println("자원 정리");
			System.out.println("종료");
		}
		*/
		while(true) {
			System.out.println("Inter 실행 중");
			if(Thread.interrupted()) {
				break;
			}
		}
		System.out.println("자원 정리");
		System.out.println("종료");
		
	}
	
	

}
package chap10.exam11.stop;

public class StopFlag extends Thread {	
	
	private boolean stop = false;
	
	public void setStop(boolean stop) {
		this.stop = stop;
	}
	
	@Override
	public void run() {		
		while(!stop) {
			System.out.println("StopFlag 실행중");
		}
		System.out.println("자원 정리");
		System.out.println("종료");
	}
}
package chap10.exam11.stop;

public class MainThread {

	public static void main(String[] args) throws InterruptedException {
		/*Stop Flag 이용*/
		StopFlag flag = new StopFlag();
		flag.start();
		Thread.sleep(1000);
		flag.setStop(true);
		
		/*강제 인터럽트 이용*/
		Inter inter = new Inter();
		inter.start();
		Thread.sleep(1000);
		inter.interrupt();//강제로 인터럽트 예외를 발생
			

	}

}


'개념 및 코딩 > 08.Thread 스레드' 카테고리의 다른 글

[JAVA]08-07.Thread Pool, Pool Block  (0) 2018.09.06
[JAVA]08-05.Demon Thread  (0) 2018.09.06
[JAVA]08-03.Priority, Synchronized  (0) 2018.09.06
[JAVA]08-02.Runnable, Thread  (0) 2018.09.04
[JAVA]08-01.Thread  (0) 2018.09.04

1.Priority


Thread에 우선순위를 부여하여 실행하는 것이다.


너무 짧은 작업이라면 우선순위 부여에 의미가 없지만 작업이 커질수록 차이가 느껴질 것이다.



package chap10.exam04.priority;

public class WorkThread extends Thread {

	public WorkThread() {
		// TODO Auto-generated constructor stub
	}

	public WorkThread(String name) {
		setName(name);
	}

	@Override
	public void run() {
		System.out.println(getName() + "의 작업 시작");
		for(int i = 0; i < 100000; i++) {
			// 이 시간동안 작업이 있다고 가정
		}
		System.out.println(getName() + "의 작업 끝");
	}

}
package chap10.exam04.priority;

public class Main {

	public static void main(String[] args) {
		// 우선 순위는 1~10까지 줄 수 있다.
		// 우선 순위가 같거나 없으면 무조건 빠른놈이 먼저다.
		// 우선 순위는 상수로도 가능하다.
		
		for(int i = 1 ; i <= 5; i++) {
			Thread th = new WorkThread(i + "번째 스레드");
			th.setPriority(i);
//			th.setPriority(Threa[d.MAX_PRIORITY);
//			th.setPriority(Thread.MIN_PRIORITY);
//			th.setPriority(Thread.NORM_PRIORITY);
			th.start();
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			// 우선순위만으로는 완벽한 스레드 제어가 어렵다
		}
	}

}



2.Synchronized


Thread는 메모리를 공유하는 특성이 있기 때문에 데이터의 간섭이 일어날 수 있다.


두 개의 thread가 돌아갈 때에 같은 데이터를 수정한다면 작업의 순서도 예측하기 어렵고 값이 어떻게 바뀔지도 모르게 된다.


Synchronized는 코드를 { }괄호로 감싸서 thread가 그 부분을 읽을 때 다른 thread에서 읽지 못하게 잠궈버린다.


먼저 들어온 thread가 작업을 마치고 다음 thread가 작업을 실행하게 된다.



package chap10.exam05.sync;

public class Computer {
	private int score;

	// 동기화 이전
	//	public void setScore(int score) {
	//		this.score = score;
	//		try {
	//			Thread.sleep(2000); // 2초 슬립 (이 사이 다른 사용자가 정보 변경 가능)
	//		} catch (InterruptedException e) {
	//			// TODO Auto-generated catch block
	//			e.printStackTrace();
	//		} 
	//		System.out.println(Thread.currentThread().getName() + " : " + this.score);
	//	}

	// 동기화 메소드 방식 : 메소드 접근을 제한
	//	public synchronized void setScore(int score) {
	//		this.score = score;
	//		try {
	//			Thread.sleep(2000); // 2초 슬립 (이 사이 다른 사용자가 정보 변경 가능)
	//		} catch (InterruptedException e) {
	//			// TODO Auto-generated catch block
	//			e.printStackTrace();
	//		} 
	//		System.out.println(Thread.currentThread().getName() + " : " + this.score);
	//	}

	// 동기화 블록 방식 : 메소드 까지는 허용, 특정 구역에서 제한

	public void setScore(int score) {
		// 스레드 들이 들어와서 작동할 수 잇음
		synchronized (this) { // this는 현재 Computer 객체
			this.score = score;
			try {
				Thread.sleep(2000); // 2초 슬립 (이 사이 다른 사용자가 정보 변경 가능)
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 
			System.out.println(Thread.currentThread().getName() + " : " + this.score);
		}
	}
}
package chap10.exam05.sync;

public class User1 extends Thread{
	private Computer com;
	public User1(Computer com) {
		setName("user 1");
		this.com = com;
	}
	@Override
	public void run() {
		com.setScore(500); // user 1은 점수를 500으로 만든다.
	}
	

}
package chap10.exam05.sync;

public class User2 extends Thread {
	private Computer com;
	
	public User2(Computer com) {
		setName("user 2");
		this.com = com;
	}

	@Override
	public void run() {
		com.setScore(100); // user2는 점수를 100으로 만든다.
	}

}
package chap10.exam05.sync;

public class PcRoom {

	public static void main(String[] args) {
		// 공용 컴퓨터 만들기
		Computer com = new Computer();
		//user1에게 컴 사용 하게 해줌
		User1 user1 = new User1(com);
		user1.start();
		//user2에게 컴 사용 하게 해줌
		User2 user2 = new User2(com);
		user2.start();
	}

}


'개념 및 코딩 > 08.Thread 스레드' 카테고리의 다른 글

[JAVA]08-07.Thread Pool, Pool Block  (0) 2018.09.06
[JAVA]08-05.Demon Thread  (0) 2018.09.06
[JAVA]08-04.Thread State, Control  (0) 2018.09.06
[JAVA]08-02.Runnable, Thread  (0) 2018.09.04
[JAVA]08-01.Thread  (0) 2018.09.04

1.Runnable, Thread


쓰레드는 전에 설명했듯이 작업을 병렬적으로 수행할 때에 사용하는데


실제로는 두개가 나란히 작동되는게 아니고 라운드로빈이라는 스케줄링 기법을 사용한다.


라운드로빈을 사용하기에 매번 똑같은 결과를 얻을 수 없고 작업 순서가 매번 다르게 실행된다.


CPU의 계산을 최대한 효율적이고 모든 프로그램에 공평하게 할당하기 위해 만들어낸 여러가지 기법들 중 하나이다.


Java에서는 Runnable인터페이스나 Thread 클래스를 상속 받아 멀티 스레드를 구현할 수 있다.


Runnable의 경우는 run메소드 작업을 정의 한 후 Thread 생성 시 안에 집에 넣는다.


Runnable로 따로 정의하지 않고 Thread의 오버라이드 된 run에 바로 정의를 하여도 문제가 없어 보통은 Thread만 단독으로 자주 쓴다.


익명객체를 사용하여 객체 선언과 동시 메소드를 1회용으로 만들수 있다.


다음 예제를 확인해보자.






package chap10.exam01.runnable;

public class Job implements Runnable {

	@Override
	public void run() {
		for(int i = 0; i < 5; i++) {
			System.out.println("워크 스레드 실행");
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

}
package chap10.exam01.runnable;

public class MainThread {

	// Main Thread를 main에서 발생 시킨다.
	// 굉장히 중요
	public static void main(String[] args) {
		
		// 함께 일해줄 WorkThread 생성(Runnable)
		// 1. work thread가 해야할 일 생성
		Runnable job = new Job();
		// 2. 일 해줄 스레드 생성
		Thread thread = new Thread(job);
		// 3. 일을 시킨다. (무조건 start로 시작함)
		thread.start();
		// 동시 실행 테스트
		for(int i = 0; i < 5; i++) {
			System.out.println("메인 스레드 실행");
			try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}



익명객체를 이용한 스레드


package chap10.exam01.runnable;

public class AnonyMain {

	public static void main(String[] args) {
		// 스레드 생성(할 일) 과 같이 생성
		// 익명객체, 따로 java파일을 만들 필요가 없음
		Thread thread = new Thread() {			
			@Override
			public void run() {
				for(int i = 0; i < 5; i++) {
					System.out.println("워크 스레드 실행");
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		};
		// 스레드 실행
		thread.start();
		
		// 메인에서 실행할 작업
		for(int i = 0; i < 5; i++) {
			System.out.println("메인 스레드 실행");
			try {
				Thread.sleep(400);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

}


'개념 및 코딩 > 08.Thread 스레드' 카테고리의 다른 글

[JAVA]08-07.Thread Pool, Pool Block  (0) 2018.09.06
[JAVA]08-05.Demon Thread  (0) 2018.09.06
[JAVA]08-04.Thread State, Control  (0) 2018.09.06
[JAVA]08-03.Priority, Synchronized  (0) 2018.09.06
[JAVA]08-01.Thread  (0) 2018.09.04

1.Thread


스레드는 작업을 수행하는 단위로 단순하게 cpu의 코어가 2개, 듀얼 코어라면 2개의 작업(스레드)을 동시에 할 수 있다.


지금까지의 작업은 싱글 스레드, 하나의 작업을 돌린것이다.


이 스레드를 이용하면 여러가지의 작업들 동시에, 병렬적으로 처리 할 수 있다.


public static void main(String[] args)가 있는 스레드를 메인 스레드라고 하고


스레드를 추가하여 병렬작업을 하는것을 멀티 스레딩이라고 한다.


멀티 스레딩의 장단점으로는 아래와 같다.


 - 장점


CPU 이용률 향상

효율적인 자원 활용

수행 기능별로 분리하여 코드가 간결해짐

어플리케이션의 응답성 향상


 - 단점


같은 프로세스의 자원을 공유하므로 동기화가 필요

동기화 처리에 관한 이슈 처리 필요(교착상태, 기아상태 등)

CPU환경이나 OS의 스케줄러를 고려해야 함

'개념 및 코딩 > 08.Thread 스레드' 카테고리의 다른 글

[JAVA]08-07.Thread Pool, Pool Block  (0) 2018.09.06
[JAVA]08-05.Demon Thread  (0) 2018.09.06
[JAVA]08-04.Thread State, Control  (0) 2018.09.06
[JAVA]08-03.Priority, Synchronized  (0) 2018.09.06
[JAVA]08-02.Runnable, Thread  (0) 2018.09.04

+ Recent posts