본문 바로가기

자바(JAVA)
[자바(Java)] 쓰레드(Thread)

// 쓰레드(thread)

- 프로세스 내에서 실제 작업을 수행. 모든  프로세스는 최소한 하나의 쓰레드를 가지고 있음.
    ~ 프로세스: 실행중인 프로그램. 자원과 쓰레드로 구성됨.

- main 쓰레드 : main 메서드의 코드를 수행하는 쓰레드

- 사용자 쓰레드 / 데몬 쓰레드
    ~ 실행중인 사용자 쓰레드가 하나도 없을 때 프로그램 종료.


// 멀티쓰레드

- 둘 이상의 쓰레드

- 장점
① 시스템 자원을 보다 효율적으로 사용할 수 있음.
② 사용자에 대한 응답성이 향상됨.
③ 작업이 분리되어 코드가 간결해짐.

- 단점
① 동기화에 주의해야 함.
② 교착상태가 발생하지 않도록 주의해야 함.
각 쓰레드가 효율적으로 고르게 실행될 수 있게 해야 함.


// 쓰레드의 구현 및 실행

- 쓰레드를 생성한 후에 start()를 호출해야 쓰레드가 작업을 시작한다.

① Thread 클래스 상속

//구현
class MyThread extends Thread {
	public void run() { // Thread 클래스의 run()을 오버라이딩
    		...
    }
}

 

// 실행
MyThread t1 = new MyThread(); // 쓰레드 생성
t1.start(); // 쓰레드 실행


Runnable 인터페이스 구현 (더 나음!)

- static Thread currenThread() : 현재 실행중인 쓰레드의 참조 반환

- String getName() : 쓰레드의 이름 반환

// 구현
class MyThread2 implements Runnable {
	public void run() { // Runnable 인터페이스의 추상메서드 run() 구현
    		...
    }
}

 

// 실행
Runnable r = new MyThread2();
Thread t2 = new THread(r) 
// 한 줄로 줄이면: Thread t2 = new Thread(new MyThread2());
t2.start();

- 싱글 쓰레드

① Thread 클래스 상속

public class Ex01 {
	public static void main(String[] args) {
		ThreadEx1 t1 = new ThreadEx1();
		
		t1.start();
	}
} // end of Ex01 class

class ThreadEx1 extends Thread{
	public void run() {
		for(int i = 0; i < 5; i++) {
			System.out.println(this.getName());
		}
	}
}// end of ThreadEx1 class

//extends Thread
class Task1 extends Thread {
	@Override
	public void run() { //SIGNATURE
		System.out.print("\nTask1 Started");
		
		for(int i= 101; i <= 199; i++) {
			System.out.print(i + " ");
		}
		
		System.out.print("\nTask1 Done");
	}
}
	
public class ThreadBasicsRunner {
	
	public static void main(String[] args) {

		//Task1
		System.out.print("\nTask1 Kicked Off");
		
		Task1 task1 = new Task1();
		task1.start(); //※task1.done() X
		
		//Task2
		System.out.print("\nTask2 Kicked Off");
				
		for(int i= 201; i <= 299; i++) {
			System.out.print(i + " ");
		}
		
		System.out.print("\nTask2 Done");
		
		//Task3
		System.out.print("\nTask3 Kicked Off");
		
		for(int i= 301; i <= 399; i++) {
			System.out.print(i + " ");
		}
		
		System.out.print("\nTask3 Done");
		
		System.out.print("\nMain Done");
	} //main
}


 Runnable 인터페이스 구현

public class Ex01 {
	public static void main(String[] args) {
		Runnable r = new ThreadEx2();
		Thread t2 = new Thread(r);
		
		t2.start();	
	}
} // end of Ex01 class

class ThreadEx2 implements Runnable{
	public void run() {
		for(int i = 0; i < 5; i++) {
			System.out.println(Thread.currentThread().getName());
		}
	}
}// end of ThreadEx2 class

//extends Thread
class Task1 extends Thread {
	@Override
	public void run() { //SIGNATURE
		System.out.print("\nTask1 Started");
		
		for(int i= 101; i <= 199; i++) {
			System.out.print(i + " ");
		}
		
		System.out.print("\nTask1 Done");
	}
}

class Task2 implements Runnable {

	@Override
	public void run() {
		System.out.print("\nTask2 Started");

		for(int i= 201; i <= 299; i++) {
			System.out.print(i + " ");
		}

		System.out.print("\nTask2 Done");
	}
	
}
	
public class ThreadBasicsRunner {
	
	public static void main(String[] args) {

		//Task1
		System.out.print("\nTask1 Kicked Off");
		
		Task1 task1 = new Task1();
		task1.start(); //※task1.done() X
		
		//Task2
		System.out.print("\nTask2 Kicked Off");
				
		Task2 task2 = new Task2();
		Thread task2Thread = new Thread(task2);
		task2Thread.start();
		
		//Task3
		System.out.print("\nTask3 Kicked Off");
		
		for(int i= 301; i <= 399; i++) {
			System.out.print(i + " ");
		}
		
		System.out.print("\nTask3 Done");
		
		System.out.print("\nMain Done");
	} //main
}

- 멀티쓰레드

public class Ex01 {
	public static void main(String[] args) {
		ThreadEx1 t1 = new ThreadEx1();
		
		Runnable r = new ThreadEx2();
		Thread t2 = new Thread(r);
		
		t1.start();
		t2.start();	
	}
} // end of Ex01 class

class ThreadEx1 extends Thread{
	public void run() {
		for(int i = 0; i < 5; i++) {
			System.out.println(this.getName());
		}
	}
}// end of ThreadEx1 class

class ThreadEx2 implements Runnable{
	public void run() {
		for(int i = 0; i < 5; i++) {
			System.out.println(Thread.currentThread().getName());
		}
	}
}// end of ThreadEx2 class2

- 싱글 쓰레드

public class Ex02 {
	public static void main(String[] args) {
		long startTime = System.currentTimeMillis();
		
		for(int i = 0; i < 300; i++) {
			System.out.printf("%s", new String("-"));
		}
		
		System.out.println("소요시간1 : " + (System.currentTimeMillis() - startTime));
		
		for(int i = 0; i < 300; i++) {
			System.out.printf("%s", new String("|"));
		}
		
		System.out.println("소요시간2 : " + (System.currentTimeMillis() - startTime));
	}
}

- 멀티 쓰레드

public class Ex03 {
	static long startTime = 0;
	
	public static void main(String[] args) {		
		startTime = System.currentTimeMillis();
		
		Thread t1 = new Thread(new MyThread1());
		Thread t2 = new Thread(new MyThread2());
		
		t1.start();
		t2.start();
	}
}

class MyThread1 implements Runnable {
	public void run() {
		for(int i = 0; i < 300; i++) {
			System.out.printf("%s", new String("-"));
		}
		System.out.println("소요시간1 : " + (System.currentTimeMillis() - Ex03.startTime));
	}
}

class MyThread2 implements Runnable {
	public void run() {
		for(int i = 0; i < 300; i++) {
			System.out.printf("%s", new String("|"));
		}
		System.out.println("소요시간2 : " + (System.currentTimeMillis() - Ex03.startTime));
	}
}

// 쓰레드의 I/O 블락킹

- 싱글 쓰레드의 경우, 사용자로부터 입력을 기다리는 동안 아무 일도 하지 않는다.

- 멀티 쓰레드의 경우, 사용자로부터 입력을 기다리는 동안 다른 쓰레드가 수행된다.


- 싱글 쓰레드

import javax.swing.JOptionPane;

public class Ex04 {
	public static void main(String[] args) {
		String input = JOptionPane.showInputDialog("아무 값이나 입력하세요.");
		System.out.println("입력하신 값은 " + input + "입니다.");
		
		for(int i = 10; i > 0; i--) {
			System.out.println(i);
			try {
				Thread.sleep(1000);
			} catch(Exception e) {}
		} // end of for
	} // end of main method
	
}// end of Ex04 class

- 멀티 쓰레드

import javax.swing.JOptionPane;

public class Ex05 {
	public static void main(String[] args) {
		Thread t1 = new Thread(new Thread05());
		
		t1.start();
		
		String input = JOptionPane.showInputDialog("아무 값이나 입력하세요.");
		System.out.println("입력하신 값은 " + input + "입니다.");
	}
}	

class Thread05 implements Runnable {
	public void run() {
		for(int i = 10; i > 0; i--) {
			System.out.println(i);
			try {
				Thread.sleep(1000);
			} catch(Exception e) {}
		} // end of for()
	} // end of run()
} // end of Thred05

// 우선 순위

- 작업의 중요도에 따라 쓰레드의 우선순위를 다르게 하여 특정 쓰레드가 더 많은 작업시간을 갖게 할 수 있다.

- void setPriority(int newPriority) : 쓰레드의 우선순위를 지정한 값으로 변경

- int getPriority() : 쓰레드의 우선순위 반환

- 최대우선순위 = 10
- 최소우선순위 = 1
- 보통우선순위 = 5

 ※ request의 의미이지, 의무가 아니다. 설정한 우선순위대로 실행될 수도 있고, 아닐 때도 있다.


public class Ex06 {
	public static void main(String[] args) {
		Thread t1 = new Thread(new Thread1());
		Thread t2 = new Thread(new Thread2());
		
		t2.setPriority(7);
		
		System.out.println("Priority of t1(-) : " + t1.getPriority());
		System.out.println("Priority of t2(|) : " + t2.getPriority());
		
		t1.start();
		t2.start();
	}
}

class Thread1 implements Runnable {
	public void run() {
		for(int i = 0; i < 300; i++) {
			System.out.print("-");
			for(int x = 0; x < 10000000; x++) {} //시간 지연용
		}
	}	
}

class Thread2 implements Runnable {
	public void run() {
		for(int i = 0; i < 300; i++) {
			System.out.print("|");
			for(int x = 0; x < 10000000; x++) {} //시간 지연용
		}
	}	
}

//extends Thread
class Task1 extends Thread {
	@Override
	public void run() { //SIGNATURE
		System.out.print("\nTask1 Started");
		
		for(int i= 101; i <= 199; i++) {
			System.out.print(i + " ");
		}
		
		System.out.print("\nTask1 Done");
	}
}

class Task2 implements Runnable {

	@Override
	public void run() {
		System.out.print("\nTask2 Started");

		for(int i= 201; i <= 299; i++) {
			System.out.print(i + " ");
		}

		System.out.print("\nTask2 Done");
	}
	
}
	
public class ThreadBasicsRunner {
	
	public static void main(String[] args) {

		//Task1
		System.out.print("\nTask1 Kicked Off");
		
		Task1 task1 = new Task1();
		task1.setPriority(1);
		task1.start(); //※task1.done() X
		
		//Task2
		System.out.print("\nTask2 Kicked Off");
				
		Task2 task2 = new Task2();
		Thread task2Thread = new Thread(task2);
		task2Thread.setPriority(10);
		task2Thread.start();
		
		//Task3
		System.out.print("\nTask3 Kicked Off");
		
		for(int i= 301; i <= 399; i++) {
			System.out.print(i + " ");
		}
		
		System.out.print("\nTask3 Done");
		
		System.out.print("\nMain Done");
	} //main
}

// 쓰레드 그룹

- 서로 관련된 쓰레드를 그룹으로 묶어서 다루기 위한 것.

- 모든 쓰레드는 반드시 하나의 쓰레드 그룹에 포함되어 있어야 함.

- 쓰레드 그룹을 지정하지 않고 생성한 쓰레드는 main 쓰레드 그룹에 속함.

- 자신을 생성한 쓰레드(부모 쓰레드)의 그룹과 우선순위를 상속받음.


- 쓰레드 자신이 속한 쓰레드 그룹을 반환

ThreadGroup getThreadgroup()

- 처리되지 않은 예외에 의해 쓰레드 그룹의 쓰레드가 종료되었을 때, JVM에 의해 이 메서드가 자동적으로 호출됨.

void uncaughtException(Thread t, Throwble e)

- 쓰레드 그룹에 포함된 활성상태에 있는 쓰레드/쓰레드 그룹의 수를 반환

int activeCount()
int activeGroupCount()

- 현재 실행중인 쓰레드가 쓰레드 그룹을 변경할 권한이 있는지 체크

void checkAccess()

- 쓰레드 그룹과 하위 쓰레드 그룹까지 모두 삭제 // 비어있어야 삭제 가능!

void destroy()

- 쓰레드 그룹의 최대우선순위 반환

int getMaxPriority()

- 쓰레드 그룹의 이름 반환

String getName()

- 쓰레드 그룹의 상위 쓰레드 그룹 반환

ThreadGroup getParent()

 

- 쓰레드 그룹에 속한 모든 쓰레드를 interrupt

void interrupt()

- 쓰레드 그룹에 속한 쓰레드와 하위 쓰레드 그룹에 대한 정보 출력

void list()


- 지정된 쓰레드 그룹의 상위 쓰레드 그룹인지 확인

boolean parentOf(ThreadGroup g)

- 쓰레드 그룹의 최대 우선순위를 설정

void setMaxPriority(int pri)


//데몬 쓰레드

- 일반 쓰레드의 작업을 돕는 보조적인 역할 수행.

- 일반  쓰레드가 모두 종료되면 자동적으로 종료.

- 가비지 컬렉터, 자동저장, 화면 자동갱신 등에 사용됨.

- 무한루프와 조건문을 이용해서 실행 후 대기하다가 특정 조건이 만족되면 작업을 수행하고 다시 대기하도록 작성.

public void run() {
	while(true) {
    	try {
        	Thread.sleep(3 * 1000); //3초마다
        } catch(InterruptedException e) {}
        
        // autoSave의 값이 true 이면 autoSave() 호출
        if(autoSave) {
        	autoSave();
        }
    }
}

 
- 쓰레드가 데몬 쓰레드 그룹인지 확인. 데몬 쓰레드이면 true 반환

boolean isDaemon()

- 쓰레드를 데몬 쓰레드 또는 사용자 쓰레드로 변경. 매개변수를 true로 지정하면 데몬 쓰레드가 됨.

void setDaemon(boolean on)

    ~ 반드시 start()를 호출하기 전에 실행되어야 함!! 안 그러면 IllegalThreadStateException 발생.


public class Ex07 implements Runnable {
	static boolean autoSave = false;
	
	public static void main(String[] args) {
		Thread t = new Thread(new Ex07());
		t.setDaemon(true); // 이 부분이 없으면 종료되지 않음!
		t.start();
		
		for(int i = 1; i <= 10; i++) {
			try {
				Thread.sleep(1000);
			} catch(InterruptedException e) {}
			
			System.out.println(i);
			
			if(i == 5) { autoSave = true; }
		} // end of for()
		
		System.out.println("프로그램을 종료합니다.");
	}// end of main()
	
	public void run() {
		while(true) {
			try {
				Thread.sleep(3 * 1000); 
			} catch(InterruptedException e) {}
			
			if(autoSave) { autoSave(); }
		} // end of while()
	} // end of run()
	
	public void autoSave() {
		System.out.println("작업 파일이 자동 저장되었습니다.");
	} // end of autoSave()
	
}// end of class

// 쓰레드의 상태

① NEW : 쓰레드가 생성되고 아직 start()가 호출되지 않은 상태
② RUNNABLE : 실행 중. 또는 실행 가능한 상태
③ BLOCKED : 동기화 블럭에 의해 일시정지된 상태
④ WAITING : 쓰레드의 작업이 종료되지는 않았지만 실행 가능하지 않은 일시정지 상태.
      ~ TIMED_WAITING :  일시정지 시간이 지정된 경우
⑤ TERMINATED : 쓰레드의 작업이 종료된 상태


// 쓰레드의 실행제어

- sleep() : 지정된 시간 동안 쓰레드를 일시정지시킴. 지정한 시간이 지나면 자동적으로 다시 실행 대기상태가 됨.
    ~  예외처리를 해야 한다.(InterruptedException이 발생하면 깨어남.)
    ~  특정 쓰레드를 지정해서 멈추게 하는 것은 불가능.

static void sleep(long millis)
static void sleep(long millis, int nanos)


- interrupt() : 대기상태(WAITING)인 쓰레드를 실행 대기상태(RUNNABLE)로 만든다.
    ~  적절히 사용하면 응답성과 효율성을 높일 수 있음!

void interrupt() //쓰레드의 interrupted상태를 false에서 true로 변경
boolean isInterrupted() // 쓰레드의 interrupted 상태를 반환
static boolean interrupted() // 현재 쓰레드의 interrupted 상태를 알려주고, false로 초기화


- suspend() : 쓰레드를 일시정지시킴. 
- resume() : suspend()에 의해 일시정지 상태에 있는 쓰레드를 실행 대기상태로 만듦.(재개)
- stop() : 쓰레드를 즉시 종료시킴.

    ~  @deprecated : 교착상태에 빠지기 쉬움.

void suspend()
void resume()
void stop()


-  join() : 지정된 시간 동안 특정 쓰레드가 작업하는 것을 기다림.
    ~  예외처리를 해야 한다.(InterruptedException이 발생하면 작업 재개)

void join() // 작업이 모두 끝날 때까지
void join(long millis) // 천분의 일초 동안
void join(long millis, int nanos) // 천분의 일초 + 나노초 동안


- yield() : 남은 시간을 다음 쓰레드에게 양보하고 자신은 실행 대기상태가 됨.
    ~  적절히 사용하면 응답성과 효율성을 높일 수 있음!

 static void  yield()


※ static 메서드는 자기 자신에게만 적용 가능


- sleep()

public class Ex08 {
	public static void main(String[] args) {
		Thread t1 = new Thread(new Thread01());
		Thread t2 = new Thread(new Thread02());
		
		t1.start();
		t2.start();
		
		delay(2*1000);
		
		System.out.print("<<main 종료>>");
	}
	
	static void delay(long millis) {
		try {
			Thread.sleep(millis); // 호출한 쓰레드를 2초 동안 잠자게
		} catch(InterruptedException e) {}
	}
}

class Thread01 implements Runnable {
	public void run() {
		for(int i = 0; i < 300; i++) {
			System.out.print("-");
		}
		System.out.print("<<t1 종료>>");
	}
}

class Thread02 implements Runnable {
	public void run() {
		for(int i = 0; i < 300; i++) {
			System.out.print("|");
		}
		System.out.print("<<t2 종료>>");
	}
}

- interrupt()

import javax.swing.JOptionPane;

public class Ex09 {

	public static void main(String[] args) {
		Thread09_1 t1 = new Thread09_1();
		t1.start();
	
		String input = JOptionPane.showInputDialog("아무 값이나 입력하세요.");
		System.out.println("입력하신 값은 " + input + "입니다.");
		t1.interrupt();//true
		System.out.println("isInterrupted() : " + t1.isInterrupted());
	}

}

class Thread09_1 extends Thread {
	public void run() {
		int i = 10;
		
		while(i!=0 && !isInterrupted()) {
			System.out.println(i--);
			for(long x = 0; x < 9900000000000000L; x++) {} // 시간 지연
		}
		System.out.println("카운트가 종료되었습니다.");
	}
}

- join()

public class Ex10 {
	static long startTime = 0;
	
	public static void main(String[] args) {
		Thread t1 = new Thread(new ThreadEx01());
		Thread t2 = new Thread(new ThreadEx02());
		
		t1.start();
		t2.start();
		
		startTime = System.currentTimeMillis();
		
		try {
			t1.join(); // main 쓰레드가 t1의 작업이 끝날 때까지 기다림.
			t2.join(); // main 쓰레드가 t2의 작업이 끝날 때까지 기다림.
		} catch(InterruptedException e) {}
		
		System.out.println("소요시간 : " + (System.currentTimeMillis() - Ex10.startTime));
	}
	
}

class ThreadEx01 implements Runnable {
	public void run() {
		for(int i = 0; i < 300; i++) {
			System.out.print(new String("-"));
		}
	} // end of run()
}

class ThreadEx02 implements Runnable {
	public void run() {
		for(int i = 0; i < 300; i++) {
			System.out.print(new String("|"));
		}
	} // end of run()
}

//extends Thread
class Task1 extends Thread {
	@Override
	public void run() { //SIGNATURE
		System.out.print("\nTask1 Started");
		
		for(int i= 101; i <= 199; i++) {
			System.out.print(i + " ");
		}
		
		System.out.print("\nTask1 Done");
	}
}

class Task2 implements Runnable {

	@Override
	public void run() {
		System.out.print("\nTask2 Started");

		for(int i= 201; i <= 299; i++) {
			System.out.print(i + " ");
		}

		System.out.print("\nTask2 Done");
	}
	
}
	
public class ThreadBasicsRunner {
	
	public static void main(String[] args) throws InterruptedException {

		//Task1
		System.out.print("\nTask1 Kicked Off");
		
		Task1 task1 = new Task1();
		task1.setPriority(1);
		task1.start(); //※task1.done() X
		
		//Task2
		System.out.print("\nTask2 Kicked Off");
				
		Task2 task2 = new Task2();
		Thread task2Thread = new Thread(task2);
		task2Thread.setPriority(10);
		task2Thread.start();
		
		//wait for tas1 to complete
		task1.join(); //task1 thread가 끝날 때까지 대기
		task2Thread.join(); //task1 thread가 끝날 때까지 대기
		
		//Task3
		System.out.print("\nTask3 Kicked Off");
		
		for(int i= 301; i <= 399; i++) {
			System.out.print(i + " ");
		}
		
		System.out.print("\nTask3 Done");
		
		System.out.print("\nMain Done");
	} //main
}

- yield()

public class Ex11 implements Runnable {

	boolean suspended = false;
	boolean stopped = false;
	
	Thread t1;
	
	Ex11(String name) {
		t1 = new Thread(this, name);
	}
	
	public void run() {
		while(!stopped) {
			if(!suspended) {
				/*
				 	작업 수행
				 */
				try {
					Thread.sleep(1000);
				} catch(InterruptedException e) {}
			} else {
				Thread.yield();
			} // end of if - else
		}// end of while
	}// end of run()
	
}

// 쓰레드의 동기화(synchronization)

- 한 쓰레드가 진행 중인 작업을 다른 쓰레드가 간섭하지 못하게 막는 것

- 동기화하려면 간섭 받지 않아야 하는 문장들을 임계 영역으로 설정

- 임계 영역은 락을 얻은 단 하나의 쓰레드만 출입 가능(객체 1 : 락 1)

- 임계 영역의 개수를 최소화 하는 것이 좋음.

- synchronized를 이용한 동기화

① 메서드 전체를 임계 영역으로 지정

public synchronized void calcSum() {
		...
}

 

public synchronized void withdraw(int money) {
	if(balance >= money) {
    	try {
        	Thread.sleep(1000);
        } catch(Exception e) {}
        
        balance -= money ;
    }
}


② 특정한 영역을 임계 영역으로 지정(더 나음)

synchronized(객체의  참조변수) {
		...
}

 

public void withdraw(int money) {
    synchronized(this) {
        if(balance >= money) {
            try {
                Thread.sleep(1000);
            } catch(Exception e) {}
		        
            balance -= money ;
        }
    }
}


- wait(), notify() : 동기화의 효율을 높여줌. Object 클래스에 정의되어 있으며, 동기화 블록 내에서만 사용할 수 있음.
    ~ wait() : 객체의 lock을 풀고 쓰레드를 해당 객체의 waiting pool에 넣음.
    ~ notify() : waiting pool에서 대기 중인 쓰레드 중의 하나를 깨움.
    ~ notifyAll() : waiting pool에서 대기 중인 모든 쓰레드를 깨움.



- 특정한 영역을 임계 영역으로 지정

public class Ex12 {
	public static void main(String[] args) {
		Runnable r = new Thread12();
		new Thread(r).start();
		new Thread(r).start();
	}
}

class Account2 {
	private int balance = 1000; //private으로 해야 동기화가 의미 有
	
	public int getBalance() {
		return balance;
	}
	
	public synchronized void withdraw(int money) {
		if(balance >= money) {
			try { Thread.sleep(1000); } catch(InterruptedException e) {}
			balance -= money;
		}
	}
}

class Thread12 implements Runnable {
	Account2 acc = new Account2();
	
	public void run() {
		while(acc.getBalance() > 0) {
			int money = (int)(Math.random() * 3 + 1) * 100;
			acc.withdraw(money);
			System.out.println("balance: " + acc.getBalance());
		}
	}
}

 

- wait(), notify()

import java.util.ArrayList;

class Customer implements Runnable {
	
	private Table table;
	private String food;
	
	Customer(Table table, String food) {
		this.table = table;
		this.food = food;
	}
	
	public void run() {
		while(true) {
			try { Thread.sleep(100); } catch (InterruptedException e) {}
			String name = Thread.currentThread().getName();
			
			table.remove(food);
			System.out.println(name + " ate a " + food);
		}
	} // end of run()
	
} // end of Customer class

class Cook implements Runnable {
	
	private Table table;
	
	Cook(Table table) { this.table = table; }
	
	public void run() {
		while(true) {
			int idx = (int)(Math.random() * table.dishNum());
			table.add(table.dishNames[idx]);
			try { Thread.sleep(10); } catch(InterruptedException e) {}
		}
	} //end of run()
	
}// end of Cook class

class Table {
	
	String[] dishNames = {"donut", "donut", "burger"};
	final int MAX_FOOD = 6;
	private ArrayList<String> dishes = new ArrayList<>();
	
	public synchronized void add(String dish) {
		while(dishes.size() >= MAX_FOOD) {
			String name = Thread.currentThread().getName();
			System.out.println(name + " is waiting.");
			try {
				wait();
				Thread.sleep(500);
			} catch(InterruptedException e) {}
		} // end of while()
		
		dishes.add(dish);
		notify();
		System.out.println("Dishes: " + dishes.toString());
	}// end of add()
	
	public void remove(String dishName) {
		synchronized(this) {
			String name = Thread.currentThread().getName();
			
			while(dishes.size() == 0) {
				System.out.println(name + " is waiting.");
				try {
					wait();
					Thread.sleep(500);
				} catch(InterruptedException e) {}
			} //end of while()
			
			while(true) {
				for(int i = 0; i < dishes.size(); i++) {
					if(dishName.equals(dishes.get(i))) {
						dishes.remove(i);
						notify();
						return;
					}
				}
				
				try {
					System.out.println(name + "is waiting.");
					wait();
					Thread.sleep(500);
				} catch(InterruptedException e) {}
				
			} // end of while(true)			
		} // end of synchronized(this)		
	}// end of remove()
	
	public int dishNum() { return dishNames.length; }

} // end of Table class

public class Ex13 {
	public static void main(String[] args) throws InterruptedException {
		Table table = new Table();
		
		new Thread(new Cook(table), "COOK").start();
		new Thread(new Customer(table, "donut"), "CUST1").start();
		new Thread(new Customer(table, "burger"), "CUST2").start();
		
		Thread.sleep(2000);
		System.exit(0);
	}
}

- extends Thread

//extends Thread
class Task1 extends Thread {
	@Override
	public void run() { //SIGNATURE
		System.out.print("\nTask1 Started");
		
		for(int i= 101; i <= 199; i++) {
			System.out.print(i + " ");
		}
		
		System.out.print("\nTask1 Done");
	}
}
	
public class ThreadBasicsRunner {
	
	public static void main(String[] args) {

		//Task1
		System.out.print("\nTask1 Kicked Off");
		
		Task1 task1 = new Task1();
		task1.start(); //※task1.done() X
		
		//Task2
		System.out.print("\nTask2 Kicked Off");
				
		for(int i= 201; i <= 299; i++) {
			System.out.print(i + " ");
		}
		
		System.out.print("\nTask2 Done");
		
		//Task3
		System.out.print("\nTask3 Kicked Off");
		
		for(int i= 301; i <= 399; i++) {
			System.out.print(i + " ");
		}
		
		System.out.print("\nTask3 Done");
		
		System.out.print("\nMain Done");
	} //main
}

- implents Runnable

//extends Thread
class Task1 extends Thread {
	@Override
	public void run() { //SIGNATURE
		System.out.print("\nTask1 Started");
		
		for(int i= 101; i <= 199; i++) {
			System.out.print(i + " ");
		}
		
		System.out.print("\nTask1 Done");
	}
}

class Task2 implements Runnable {

	@Override
	public void run() {
		System.out.print("\nTask2 Started");

		for(int i= 201; i <= 299; i++) {
			System.out.print(i + " ");
		}

		System.out.print("\nTask2 Done");
	}
	
}
	
public class ThreadBasicsRunner {
	
	public static void main(String[] args) {

		//Task1
		System.out.print("\nTask1 Kicked Off");
		
		Task1 task1 = new Task1();
		task1.start(); //※task1.done() X
		
		//Task2
		System.out.print("\nTask2 Kicked Off");
				
		Task2 task2 = new Task2();
		Thread task2Thread = new Thread(task2);
		task2Thread.start();
		
		//Task3
		System.out.print("\nTask3 Kicked Off");
		
		for(int i= 301; i <= 399; i++) {
			System.out.print(i + " ");
		}
		
		System.out.print("\nTask3 Done");
		
		System.out.print("\nMain Done");
	} //main
}

// ExecutorService

- 한번에 하나만 실행
    ~ ExecutorService.executorService = Excutors.newSingleThreadExecutor();  > 한번에 하나의 스레드만 동작
       executorService.execute(스레드) : 스레드를 실행함.
       
executorService.shutdown() : 프로그램을 멈춰줌. (그렇지 않으면 작업이 끝나도 계속 동작함)

- 동시에 여러 개 실행
    ~ ExecutorService executorService = Executors.newFixedThreadPool(동시에 실행될 수);
       executorService.execute(스레드) 
       
executorService.shutdown() 


- 한번에 하나만 실행

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

public class ExecutorServiceRunner {

	public static void main(String[] args) {

		ExecutorService executorService = Executors.newSingleThreadExecutor();
		executorService.execute(new Task1());
		executorService.execute(new Thread(new Task2()));

		//Task3
		System.out.print("\nTask3 Kicked Off");

		for(int i= 301; i <= 399; i++) {
			System.out.print(i + " ");
		}

		System.out.print("\nTask3 Done");

		System.out.print("\nMain Done");
		
		executorService.shutdown(); //프로그램을 멈춰줘야 함.

	} //main
}

- 동시에 여러 개 실행

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

class Task extends Thread {
	
	private int number;
	
	public Task(int number) {
		this.number = number;
	}
	
	@Override
	public void run() { //SIGNATURE
		System.out.print("\nTask" + number + " Started");
		
		for(int i= number*100; i <= number*100 + 99; i++) {
			System.out.print(i + " ");
		}
		
		System.out.print("\nTask1 Done");
	}
	
}

public class ExecutorServiceRunner {

	public static void main(String[] args) {

//		ExecutorService executorService = Executors.newSingleThreadExecutor();
		ExecutorService executorService = Executors.newFixedThreadPool(5);
		executorService.execute(new Task(1));
		executorService.execute(new Task(2));
		executorService.execute(new Task(3));
		executorService.execute(new Task(4));

		//Task3
		System.out.print("\nTask3 Kicked Off");

		for(int i= 301; i <= 399; i++) {
			System.out.print(i + " ");
		}

		System.out.print("\nTask3 Done");

		System.out.print("\nMain Done");
		
		executorService.shutdown(); //프로그램을 멈춰줘야 함.

	} //main
}

// Callable로 후처리 스레드에서 돌아오기

1. Future<자료형> 변수명 =  executorService.submit(Callable 구현 클래스);

2. 변수명.get() : Callable 구현 클래스 코드 실행값 반환

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;

class CallableTask implements Callable<String> {

	private String name;
	
	public CallableTask(String name) {
		this.name = name;
	}
	
	
	@Override
	public String call() throws Exception {
		Thread.sleep(1000);
		return "Hello " + name;
	}
	
}

public class CallableRunner {
	
	public static void main(String[] args) throws InterruptedException, ExecutionException {

		ExecutorService executorService = Executors.newFixedThreadPool(1);
		
		Future<String> welcomeFuture =
				executorService.submit(new CallableTask("나무의 하루"));
		System.out.println("new CallableTask(\"나무의 하루\") executed");
		
		String welcomeMessage = welcomeFuture.get();
		
		System.out.println(welcomeMessage);
		
		System.out.println("Main complete");
		
		executorService.shutdown();

	} //main

}

 


- executorService.invokeAll() : 여러 개의 스레드 작업이 모두 끝날 때까지 대기 후 실행

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

public class MultipleCallableRunner {

	public static void main(String[] args) throws InterruptedException, ExecutionException {

		ExecutorService executorService = Executors.newFixedThreadPool(3);

		List<CallableTask> tasks= List.of(new CallableTask("나무의 하루")
									, new CallableTask("Hanee")
									, new CallableTask("아침햇살"));
		
		List<Future<String>> results = executorService.invokeAll(tasks);
		
		for(Future<String> result:results) {
			System.out.println(result.get());
		}
		
		executorService.shutdown();

	} //main
	
}

 


- executorService.invokeAny() : 가장 빠르게 완료된 스레들의 결과 하나만 반환

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

public class MultipleAnyCallableRunner {

	public static void main(String[] args) throws InterruptedException, ExecutionException {

		ExecutorService executorService = Executors.newFixedThreadPool(3);

		List<CallableTask> tasks= List.of(new CallableTask("나무의 하루")
									, new CallableTask("Hanee")
									, new CallableTask("아침햇살"));
		
		String result = executorService.invokeAny(tasks);
		
		
		System.out.println(result);
		
		executorService.shutdown();

	} //main
	
}

'자바(JAVA)' 카테고리의 다른 글

[자바(Java)] 스트림(stream)  (0) 2022.12.20
[자바(Java)] 람다식  (0) 2022.12.20
[자바(Java)] 애너테이션  (0) 2022.12.17
[자바(Java)] 열거형(enum)  (0) 2022.12.17
[자바(Java)] 제네릭(Generics)  (1) 2022.12.17