Frudy
6k
2019-03-20 16:53:36 작성 2019-03-20 16:59:23 수정됨
2
627

자바 쓰레드코딩에 대해 질문이있어요.


제 코드는, 똑같은 코드인데 실행할때마다 다른결과가 나오구요,

상대방의 코드는 여러번 실행해도 모두 똑같은결과가 나와요.


무슨차이인지 전혀 납득이안가서 질문올리게되었어요.


원래 쓰래드코딩이란게 미묘하게 누가먼저실행되냐에따라 다르기때문에,

누가먼저실행되더라도 같은결과를 낼 수 있도록 코딩해야하는거같은대..


test.Water.java (synchronized만 테스트해보려고 간단해요)

public class Water 
{
	private int water;

	public int getWater()
	{
		return water;
	}
}


test.Test.java

public class Test
{
	public static void main(String[] args) throws InterruptedException
	{
		Water water = new Water();
		ProducerThread3 pt = new ProducerThread3(water);
		ConsumerThread3 ct = new ConsumerThread3(water);
		
		pt.setConsumerThread(ct);
		ct.setProducerThread(pt);
		
		pt.start();
		ct.start();
	}
}


쓰레드1 - 생산자쓰래드

public class ProducerThread3 extends Thread
{
	private Water water;
	private Thread consumerThread;

	public ProducerThread3(Water water)
	{
		setName("생산자 스레드");
		this.water = water;
	}

	public void setConsumerThread(Thread consumerThread)
	{
		this.consumerThread = consumerThread;
	}

	@Override
	public void run() 
	{
		synchronized(water)
		{
			water.getWater();
			System.out.println("Producer 동기화블록 - 생산자 상태 : " + consumerThread.getState());
		}
		
		System.out.println("Producer 종료 - 생산자 상태 : " + consumerThread.getState());
	}
}


쓰레드2 - 소비자쓰레드

	@Override
	public void run() 
	{
		synchronized(water)
		{
		}

		System.out.println("Consumer Thread 종료");
	}

다른거 같아서 run()만 올렸어요.


그리고 실행결과가..

이런식으로, 아주 다양하게 나왔어요. 그래서 아주 황당한상태인데,

(wait(), notify()가 이해가안되서 이것부터 공부하다 나온 테스트코드에요)


구글링하다 찾은 다른분의 코드는..

    public class ThreadB extends Thread{
        int total;
        @Override
        public void run(){
            synchronized(this){
                for(int i=0; i<5 ; i++){
                    System.out.println(i + "를 더합니다.");
                    total += i;
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                notify();
            }
        }
    }


    public class ThreadA {
        public static void main(String[] args){
            ThreadB b = new ThreadB();
            b.start();

            synchronized(b){
                try{

                    System.out.println("b가 완료될때까지 기다립니다.");
                    b.wait();
                }catch(InterruptedException e){
                    e.printStackTrace();
                }

                System.out.println("Total is: " + b.total);
            }
        }
    }


이분의 코드는 아주 멀쩡해요. 아무리실행해도 항상같은결과에요.

무슨차이가있는거에요?...

0
  • 답변 2

  • 단설
    623
    2019-03-20 17:44:29

    똑같은 water 인스턴스를 넣었고. 생산자의 작업이 너무 짧게 걸리는 작업이라서 그렇습니다.


    생산자를 먼저 스타트했으니까 거의 무조건 생산자가 먼저 synchronized에 들어가겠죠. 

    아마 의도하신거는 소비자 쓰레드는 항상 BLOCKED 상태가 되는걸 의도하신 것 같습니다.

    그런데 생산자의 작업이 너무 짧다보니까 소비자 쓰레드가 synchronized에 접근도 못했는데 상태를 출력하고 종료해버리는 경우가 발생하는 것입니다. 그래서 RUNNABLE으로 보이는거구요. 

    만약에 생산자 쓰레드의 synchronized 안에서 Thread.sleep(1000L) 같은걸 주면 소비자 쓰레드는 항상 BLOCKED으로 나올 것입니다.

  • 단설
    623
    2019-03-20 20:49:36

    음.. 그런가요? 뭐 하긴 쓰레드 어느 쪽이 먼저 동작하느냐는 보장되지 않는 것이므로 누가 먼저 들어가는지는 중요한게 아니죠. 중요한건 그렇기 때문에 누가 먼저 시작하던지 항상 원하는 대로 동작할 수 있도록  thread safety한 코드를 짜야된다는 것입니다.


    아래 쪽 코드에 대해서 이야기해볼까요 이제.

    아마 지금 그렇게 동작하는 이유는 짐작하시는게 맞긴 할겁니다.

    하지만 아래 쪽 코드도 사실 코드만 보면 어느 쪽이 먼저 시작할지 보장되는건 아닙니다. 어떤 특별한 환경, 특별한 jvm 구현체에서는 ThreadB가 먼저 동작할 수도 있겠죠.

    그런 의미에서 저 코드는 잘못 짜여진 코드입니다. ThreadB가 먼저 동작해버리면 ThreadA의 main thread는 wait로 들어가서 아무도 깨워주지 않으니 영원한 wait에 빠져버릴 것이기 때문입니다. 저 코드는 ThreadA가 무조건 먼저 동작하는걸 전제로 짠건데 저렇게 짜시면 안 됩니다.

  • 로그인을 하시면 답변을 등록할 수 있습니다.