저를 포함한 초보자분들에게 도움이 됐으면 좋겠습니다.
초보자분들은 수박 겉핥기 식으로 알고 넘어가지 마시고, 조금이라도 왜 이렇게 프로그램이 돌아가는지 알고 가시는 시간이 됐으면 좋겠습니다.

자 오늘은 쓰레드(Thread)에 대해서 알아봅시다. 일반적으로 자바 프로그램은 코딩대로 순차적으로 수행하게 됩니다. 평소에는 프로그램에 하나의 쓰레드만 사용을 하고 있죠.
사용하기에는 문제없습니다. 하지만 여러 개의 쓰레드를 사용한다면 어떤 이점이 있을까요?
여러 개의 쓰레드가 해야 하는 일을 나누어 작업을 진행한다면, 하나의 쓰레드가 일하는 것보다 훨씬 빠르지 않을까요? 당연한 이야기입니다. 이처럼 프로세스가 병렬로 작업을 처리할 수 있기에 훨씬 좋은 성능을 낼 수 있는 거죠. 

자바에서 쓰레드 문법은 굉장히 쉽습니다. 어떻게 선언하는지부터 설명해보죠.
자바에서 쓰레드를 선언하는 방법은 2가지입니다. Thread를 상속 받든지, Runnable 인터페이스를 상속받기 2가지가 존재하죠.

(1)public class mythread extends Thread{  //Thread 상속
	public void run() { } //실질적으로 쓰레드가 할 일 구현
	public static void main(String args[]) {
		mythread test = new mythread();         
		test.start();
	}
}
(2)public class mythread implements Runnable{ //Runnable 인터페이스 
	public void run() { }
	public static void main(String args[]) {
		mythread test = new mythread();       //객체를 생성하여
		Thread myThread = new Thread(test);   // 쓰레드의 타겟에 넣어 사용
		myThread.start(); 
	}
}

(1) 코드는 Thread 클래스를 상속받아 사용합니다. 간단하게 Thread 클래스를 상속받는 자손 클래스의 객체를 만들어. start()로 쓰레드를 시작하면 됩니다. 
(2) 코드는 살짝 다릅니다. Thread 클래스를 상속받는 것이 아니기에, mythread 객체를 생성하여 
Thread 클래스 객체를 만들어 target으로 넘겨줘야 합니다. 실행 방법은 start()로 같습니다. 
대개 (1) 코드는 상속받는 클래스가 없을 때 사용하겠죠? 그럼 (2) 코드는 반대로 다른 클래스를 상속받지만, 쓰레드를 써야 하는 경우 사용할 수 있겠죠? 둘의 차이와 사용방법 차이를 기억해 두시면 좋겠네요.
실실 적으로 쓰레드가 해야 하는 일은 run(){ } 메소드를 구현해 주시면 됩니다. 여기서 의문이 하나 생길 수 있습니다. 우린 run()을 안 하고 start()를 했는데, 왜 run()이 되는 걸까요?
start() 메소드를 한번 보고 갈 필요가 있습니다. 쓰레드는 .start()를 하게 되면, run(){}메소드가 자동으로 실행되게 되는데요. 왜 그런지 알아보고 갑시다.

    public synchronized void start() { //start()메소드
        group.add(this);
        boolean started = false;
        try {
            start0();  <--------------------이 부분
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
            }
        }
    }

    private native void start0();
    @Override
    public void run() {
        if (target != null) {
            target.run(); <-----------------------실질적으로 run()메소드실행
        }
    }

위 코드는 start()메소드의 구현내용입니다. 간단하게 되어있죠? 여기서 start0()이라는 메소드를 수행하면 start0에서 target.run();을 수행하게 됩니다. target은 우리가 만들어준 객체입니다.
고로 우리가 선언한 run()메소드가 실행되는 것이죠. 혹여나 start()메소드로 쓰레드를 수행하지 않고
run()으로 한다면... 쓰레드는 생각하는 대로 수행하지 않을 것입니다. 주의하세요!

자 그럼 이제 쓰레드 예제를 하나 살펴보죠.

(3)public class mythread extends Thread{
	String name = null;
	mythread(String k){
		name =k;
	}
	public void run() {
		for(int i = 0; i <10;i++) {
				System.out.println(name + "실행중..");
		}
		
	}
	public static void main(String args[]) {
		mythread test = new mythread("A");
		mythread test2 = new mythread("B");
		test.start();
		test2.start();
	}
}

(3) 코드는 두 개의 쓰레드를 만들고, 실행시켜 보았습니다. 일반적으로 한 개의 쓰레드를 사용하게 된다면, "A 실행 중.." 10번 출력 "B 실행 중.." 10번 출력 순차대로 나올 것입니다.
하지만 쓰레드를 사용하면 어떻게 나올지 예상할 수 없습니다. 직접 해보신다면, 실행할 때마다 결과가 뒤죽박죽 섞여서 나오게 됩니다. 동시에 수행하고 있다고 봐도 무방하죠.

하나만 더 보고 마무리하겠습니다. 사무실에서는 한 대의 프린터를 여러 개의 컴퓨터에 연결하여 사용하는 경우가 많습니다. 대개 이런 경우 쓰레드를 사용하여 처리한다고 볼 수 있는데요. 하지만, 위에서 알아본 쓰레드는 뒤죽박죽 섞이기 때문에 '순서'란 개념이 없습니다. 이렇게 된다면, 프린트 물이 뒤죽박죽 섞여 출력되지 않을까요?


(4)class printer{
	private int count = 0;
	synchronized void count_inc() {
		count++;
		System.out.println(count + "번 출력중..");
	}
}
===============================================================
public class mythread extends Thread{
	printer k = null;
	mythread(printer a) {
		this.k = a;
	}
	public void run() {
		for(int i = 0; i <10;i++) {
				k.count_inc();
		}
		
	}
	public static void main(String args[]) {
		printer temp = new printer();
		mythread test = new mythread(temp);
		mythread test2 = new mythread(temp);
		test.start();
		test2.start();
	}
}

(4) 코드를 봅시다. printer라는 클래스를 만들어서 다른 쓰레드(컴퓨터)에서 접근하여 출력한다고 생각해보죠. 일반적으로 쓰레드를 사용하여 구현한다면, 순서가 뒤죽박죽 섞여서 나오게 됩니다. 생각한 대로 온전하게 출력을 하지 못합니다. 이걸 막기 위해 'synchronized'라는 키워드가 있습니다.
synchronized 키워드를 메소드에 써준다면, A쓰레드가 해당 메소드를 수행할 때 다른 B쓰레드에서 
해당 메소드에 접근하면 A쓰레드가 해당 메소드를 끝낼 때까지 B쓰레드를 기다리게 만듭니다. 
synchronized 키워드를 사용해 줌으로써 순차대로 프린트는 출력을 해 줄 수 있습니다.

이외에도 notify(), sleep() ... 등등 쓰레드 관련 메소드가 많습니다. 궁금하신 분은 찾아보시는 것도 도움이 될 것 같네요. 오늘은 여기까지입니다! 주의하실 점은 start()로 쓰레드를 실행한다는 점, 또 쓰레드 선언 방법 2가지를 구분하여 알고 계시면 될 것 같습니다.

지금까지 java 이론적인 설명을 올려왔는데요. 기본적인 내용이었습니다. 이 정도 배우면 개인적인 생각으론 기본적인 내용은 다 했다고 생각 듭니다. 애플릿, 스윙.. 이런 부분은 눈에 보이는 것을 만드는 것이니까 제외하고.. 어느 정도 자바를 한번 훑어보았습니다.
하지만, 그래도 아직 알아야 할 것이 많습니다. 지금까지 전체적인 내용만 본 것이고, 이제 상세하게 살펴봐야 하겠죠. 그래서, 이제부터는 사용할 만한 함수를 정말 '뜯어보기'말에 맞게 뜯어볼 계획입니다.
물론 추가적으로 올릴 내용이 있다면 올리겠습니다. 
감사합니다.

+ Recent posts