
일꾼이 n명인 것
장점
시스템 자원을 효율적으로 사용할 수 있다
사용자에 대한 응답성이 향상된다
작업이 분리되어 코드가 간결해진다
단점
동기화에 주의해야 한다
교착상태가 발생하지 않도록 주의해야 한다
각 스레드가 효율적으로 고르게 실행될 수 있게 한다
실행되지 못하는 상태인 기아상태가 발생하지 않도록 주의해야 한다
여러 스레드가 동시에 수행되는 프로그래밍, 여러 작업이 동시에 실행되는 효과
스레드는 각각 자신만의 작업 공간을 갖는다
각 스레드 사이에서 공유하는 자원이 있을 수 있다
여러 스레드가 자원을 공유하여 작업이 수행되는 경우, 서로 자원을 차지하려는 race condition이 발생할 수 있다
자원을 차지하려는 경쟁이 발생하는 부분을 critical section(임계영역)이라고 한다
그에 대한 동기화를 구현하지 않으면 오류가 발생할 수 있다
순차적 수행, 한 스레드가 사용할 때 다른 스레드가 사용할 수 없도록 락을 거는 것
스레드의 I/O 블락킹
- 싱글 스레드 프로세스에서는 스레드 실행 중에 사용자의 입력을 받는 구간이 있다면 스레드가 아무 일도 하지 않고 입력을 기다린다
 - 멀티 스레드에서는 입력을 기다린다면 다른 스레드의 작업이 실행된다
 
class MyThread extends Thread{
	
	public void run() {
		
		int i;
		for(i = 0; i<200; i++) {
			System.out.print(i + "\t");
		}
	}
}
public class ThreadTest {
	public static void main(String[] args) {
		System.out.println(Thread.currentThread());
		MyThread th1 = new MyThread();
		th1.start();
		
		MyThread th2 = new MyThread();
		th2.start();
	}
}

public static void main(String[] args) {
    //스레드 1 : 메인 스레드는 스레드 두개 생성하고 스타트하면 끝
    System.out.println(Thread.currentThread() + " start");
    MyThread runnable = new MyThread();
    Thread th1 = new Thread(runnable); // runnable객체를 입력받을 수 있다
    Thread th2 = new Thread(runnable);
    th1.start();
    th2.start();
    
    System.out.println(Thread.currentThread() + " end");
}
- 간단하게 돌리는 방법
 Runnable run = new Runnable() { //바로 익명 객체를 불러올 수 있다 - 메서드 구현해야함 @Override public void run() { System.out.println("run"); } }; run.run();
Runnable 인터페이스를 구현하여 쓰는 것이 코드가 길어지긴 하지만 다른 클래스를 상속받아야 할 경우가 있을 수 있기 때문에 주로 사용된다

프로그램은 실행중인 사용자 스레드가 하나도 없을 때 종료된다

여러 스레드를 start하면 실행된 순서대로 스레드가 실행되지 않을 수 있다
실행 순서는 OS의 스케쥴러가 결정한다
스케쥴러에게 사용자가 원하는 실행 순서를 제안하는 방법으로 우선순위를 둔다
PriorityThread pt1 = new PriorityThread();
PriorityThread pt2 = new PriorityThread();
PriorityThread pt3 = new PriorityThread();
pt1.setPriority(Thread.MIN_PRIORITY);
pt2.setPriority(Thread.NORM_PRIORITY);
pt3.setPriority(Thread.MAX_PRIORITY);
pt1.start();
pt2.start();
pt3.start();
원하는 스레드에 우선순위를 부여할 수 있다
suspend, resume, stop deprecated 됨
일시정지 상태와 정지 상태를 boolean으로 생성하고 반복문에 적용하는 방법으로 구현

public class JoinTest extends Thread{
    int start;
    int end;
    int total;
    public JoinTest(int start, int end) {
        this.start = start;
        this.end = end;
    }
    public void run() {
        int i;
        for (i = start; i <= end; i++) {
            total += i;
        }
    }
    public static void main(String[] args) {
        System.out.println(Thread.currentThread() + "start");
        JoinTest jt1 = new JoinTest(1, 50);
        JoinTest jt2 = new JoinTest(51, 100);
        jt1.start();
        jt2.start();
		//원래는 메인이 jt1,2가 종료되기 전에 종료되어서 jt1,2가 완전히 실행되지 않고 종료된다
        //메인에서 join을 사용하면 메인은 수행을 안한다 jt1, 2가 종료될 때까지 기다린다
        try {
            jt1.join();
            jt2.join();
        } catch (InterruptedException e) { //jt1,2의 수행이 영원히 종료되지 않을 경우 메인으로 돌아오기 위해
            throw new RuntimeException(e);
        }
        int lastTotal = jt1.total + jt2.total;
        System.out.println("jt1.total = " + jt1.total);
        System.out.println("jt2.total = " + jt2.total);
        System.out.println("lastTotal = " + lastTotal);
        System.out.println(Thread.currentThread() + "end");
    }
}
public class TerminateThread extends Thread{
    private boolean flag = false;
    int i;
    public TerminateThread(String name){
        super(name);
    }
    public void run(){
        while(!flag){ //while(true)로 두지 말고 플래그를 이용해서 수정할 수 있다
            try {
                sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println( getName() + " end" );
    }
    public void setFlag(boolean flag){
        this.flag = flag;
    }
    public static void main(String[] args) throws IOException {
        TerminateThread threadA = new TerminateThread("A");
        TerminateThread threadB = new TerminateThread("B");
        TerminateThread threadC = new TerminateThread("C");
        threadA.start();
        threadB.start();
        threadC.start();
        int in;
        while(true){
            in = System.in.read();
            if ( in == 'A'){
                threadA.setFlag(true); //a 스레드 종료
            }else if(in == 'B'){ 
                threadB.setFlag(true); //b 스레드 종료
            }else if( in == 'C'){
                threadC.setFlag(true); //c 스레드 종료
            }else if( in == 'M'){
                threadA.setFlag(true); //모든 스레드 종료
                threadB.setFlag(true);
                threadC.setFlag(true);
                break;
            }else{
                System.out.println("type again");
            }
        }
        System.out.println("main end");
    }
}
멀티 스레드 프로세스에서는 다른 스레드의 작업에 영향을 미칠 수 있다
교착 현상
진행중인 작업이 다른 쓰레드에게 간섭받지 않게 하려면 동기화가 필요하다
간섭받지 않아야 하는 문장을 임계 영역으로 설정한다
임계 영역은 락(자물쇠)을 얻은 단 하나의 스레드만 출입 가능

임계 영역은 최소화 하는 것이 좋음
2번 방법으로 하는 것이 좋음

동기화를 많이 사용하면 효율이 떨어진다.
효율을 높이는 방법으로 wait()과 notify()가 사용된다



> 음식이 없을 때는 wait()으로 손님이 락을 풀고 기다리도록 함
> 손님이 음식을 먹으면 요리사에게 notify()로 통보
> 음식이 추가되면 손님에게 알려 다시 락을 획득하도록 함

결과
