멀티 스레드(2)

zooyeong·2023년 5월 23일
0

17주차

목록 보기
2/4
post-thumbnail

📌 동기화 메소드(or 동기화 블록)

💡 synchronized

synchronized 는 단 하나의 스레드만 실행할 수 있는 메소드 또는 블록이다. 다른 스레드는 메소드나 블록이 실행이 끝날 때까지 대기해야한다.

동기화 메소드

public synchronized void methid(){
	임계 영역; //단 하나의 스레드만 실행
}

동기화 블록

public void method() {
	//여러 스레드가 실행 가능한 영역
    ...
    synchronized(공유객체) {
    	임계 영역 //단 하나의 스레드만 실행
    }
    //여러 스레드가 실행 가능한 영역
	...
}

아래의 예제를 통해 synchronized를 자세히 살펴보았다.

public class ThreadWorkTest1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Work work = new Work();
		Worker worker1 = new Worker(work, 10, "1번 일꾼");
		Worker worker2 = new Worker(work, 10, "2번 일꾼");
		
		worker1.start();
		worker2.start();	
	}

}

class Work{
	int workCnt; //일거리 개수
	
	public Work() {
		workCnt = 0;
	}
	
	public void addCnt() {
		workCnt++;
	}
	
	public String toString() {
		return String.valueOf(workCnt);
	}
}

>>> 실행할 ThreadWorkTest 클래스와 Work 클래스 생성

public class Worker extends Thread{
	Work work;		//일
	int workLoad;	//얼마나 일해라
	
	public Worker(Work work, int workLoad, String name) {
		super(name); //부모클래스로 값을 넘김
		this.work = work;
		this.workLoad = workLoad;
	}
	
	//Thread로 수행되는 메소드
	public void run() {
		for(int i=1; i<=workLoad; i++) {
			
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			//스레드가 동시에 접근하지 못하고 하나씩 접근하도록 보장
			synchronized (work) {				
				work.addCnt();
			}
			
			System.out.println(Thread.currentThread().getName()
					+ " -> " + work.toString());
		}
	}
}

>>> Thread를 상속하는 클래스 Worker를 생성

▼ 실행 결과

>>> 충돌 없이 일꾼이 10번씩 두 일꾼이 총 20번을 일한 결과를 확인할 수 있음

📌 스레드 상태 제어(2)

💡 스레드 간 협업 >>> wait(), notify(), notifyAll()

스레드간 협업 메소드는 동기화 메소드 또는 블록에서만 호출 가능한 Object의 메소드이다. 두 개의 스레드가 교대로 번갈아 가며 실행해야 할 경우 주로 사용한다.

public class ProducerConsumerTest {

	public static void main(String[] args) {
		//생산자 -> 생산		소비 <- 소비자
		
		CubbyHole cubbyHole = new CubbyHole();
		Producer p1 = new Producer(cubbyHole, 1);
		Consumer c1 = new Consumer(cubbyHole, 1);
		
		p1.start();
		c1.start();
		
	}

}

>>> 실행할 ProducerConsumerTest 클래스

class Consumer extends Thread{
	CubbyHole cubbyHole;
	int number;
	
	public Consumer(CubbyHole c, int n) {
		cubbyHole = c;
		number = n;
	}
	
	public void run() {
		for(int i=1; i<=10; i++) {
			int value = cubbyHole.get();
			System.out.println("Consumer " + number + " -> get -> " +value);
		}
	}
}

>>> Thread를 상속한 Consumer 클래스

class Producer extends Thread{
	CubbyHole cubbyHole;
	int number;
	
	public Producer(CubbyHole c, int n) {
		cubbyHole = c;
		number = n;
	}
	
	public void run() {
		for(int i=1; i<=10; i++) {
			cubbyHole.put(i);
			System.out.println("Producer " + number + " -> put -> " + i);
			
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

>>> Thread를 상속한 Producer 클래스

class CubbyHole{
	int contents;
	boolean flag;
	
	public CubbyHole() {
		contents = 0;
		flag = false;
	}
	
	public synchronized int get() {
		
		while(flag == false) {
			try {
				wait(); //쓰레드 대기
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		flag = false;
		notifyAll(); //쓰레드 깨워주기
		return contents;
	}
	
	public synchronized void put(int value) {
		
		while(flag == true) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		contents = value;
		flag = true;
		notifyAll(); //쓰레드 깨워주기
	}
}

>>> Consumer와 Producer에서 생산과 소비를 함께 사용할 공용 공간인 CubbyHole 클래스

해석 : Procuder 클래스에서 put한 후 Consumer 클래스에서 get할 수 있는 코드
>>> wait()과 notifyAll() 메소드를 통해 스레드를 대기하고 깨워주기를 반복

문제사항 : 처음 put()과 get() 메소드에 synchronized를 지정해주지 않으니 아래의 오류가 발생

>>> 이를 잡아주기 위해 두 클래스에서 번갈아가며 사용할 수 있게 put()과 get() 메소드를 동기화 메소드 synchronized로 작성

▼ 실행 결과

profile
Have a good day ⌯’▾’⌯

0개의 댓글