본문 바로가기
프로그래밍 언어/JAVA

[JAVA] 스레드(Thread)

by E145 2021. 1. 20.
반응형

스레드(Thread)

 

스레드란?

 

 - 여러 가지 일을 동시에 진행하는 것처럼 보이게 할 수 있는 것.

 

 - 여러 스택이 아주 빠르게 번갈아 실행되기 때문에 여러 스택이 동시에 실행되는 것처럼 보인다.

 

 - 스레드는 JVM 스레드 스케줄러가 실행시킬 스레드를 선택했다가 다른 스레드에 기회를 주는 과정을 반복함에 따라 실행 가능한 상태와 실행중인 상태 사이를 반복한다.

 

 


 

스레드 사용 방법

 

 - 1. Runnable 객체(스레드에서 할 작업)를 만든다.

     : Runnable은 인터페이스이다. 스레드에서 할 작업은 Runnable의 run 메소드를 오버라이딩 해서 만든다.

 

 - 2. Thread 객체를 만들고 그 객체에 Runnable 객체를 전달한다.

     : 새로운 Runnable 객체를 Thread 생성자에 전달한다.

 

public class test {
    public static void main(String[] args){
        Runnable threadRun = new myRunnable();

        Thread myThread = new Thread(threadRun);
        myThread.start();

        System.out.println("메인 실행");

    }
}

class myRunnable implements Runnable{
    public  void run(){
        go();
    }
    public  void go(){
        test();
    }
    public void test(){
        System.out.println("스레드 실행!");
    }
}

실행 결과

 

 - Thread 객체는 재사용이 불가능하다. run 메소드가 일단 종료되고 나면 사망 상태(dead state)가 된다. 사망 상태의 스레드는 run 메소드의 실행이 끝나 있는 상태이며, 절대로 재시작될 수 없다.

 


 

스레드 스케줄러

 

 - 스레드 스케줄러는 어느 스레드가 실행되어야 하는지, 얼마나 오랫동안 실행되어야 하는지 등을 결정한다.

 

 - 스케줄러는 사용자 마음대로 제어할 수 없다. 스케줄링과 관련하여 어느 것도 확언할 수 없다.

 

 - 스케줄러가 어떤 특별한 방식으로 작동할 것을 가정하고 프로그램을 만들면 안 된다.

 

public class test {
    public static void main(String[] args){

        Runnable threadRun = new myRunnable();
        Thread myThread1 = new Thread(threadRun);
        Thread myThread2 = new Thread(threadRun);
        myThread1.start();
        myThread2.start();

        System.out.println("메인 실행");
    }
}


class myRunnable implements Runnable{
    public  void run(){
        go();
    }
    public void go(){
        test();
    }
    public  void test(){
        for(int i=0;i<5;i++)
        {
            System.out.println(Thread.currentThread().getName()+" 실행!"  );
            try {
                Thread.sleep(100);

            }catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 

 

여러 번 실행 결과 모두 다르게 나온다.(스레드 스케줄러를 예측할 수 없다.)

 


 

스레드 락, 동기화

 

 - 어떤 메소드를 한 번에 한 스레드만 접근할 수 있게 하고 싶으면 synchroized 키워드를 이용하여 락을 건다.

 

 - synchronized 키워드가 있는 메소드에 한 스레드가 접근하면, 다른 스레드는 해당 메소드에 접근할 수 없다.

 

public class test {
    public static void main(String[] args){

        Runnable threadRun = new myRunnable();
        Thread myThread1 = new Thread(threadRun);
        Thread myThread2 = new Thread(threadRun);
        myThread1.start();
        myThread2.start();

        System.out.println("메인 실행");
    }
}


class myRunnable implements Runnable{
    public  void run(){
        go();
    }
    public void go(){
        test();
    }
    public synchronized void test(){
        for(int i=0;i<5;i++)
        {
            System.out.println(Thread.currentThread().getName()+" 실행!"  );
            try {
                Thread.sleep(100);

            }catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

synchroized를 이용하여 한 번에 하나의 스레드만 접근한다.

 


 

스레드 동기화 주의 사항

 

 - 스레드 사용 시 전부 동기화 하면 안전할 수 있다고 생각하지만, 하지 않는것이 좋다.

   많은 동기화는 성능을 저하시킨다. 

 

 - 동기화가 꼭 필요한 부분에 synchroized 키워드를 이용하여 메소드 안의 특정한 부분만 동기화 시킨다.

 

class myRunnable implements Runnable{
    public  void run(){
        go();
    }
    public void go(){
        test();
    }
    public synchronized void test(){
        for(int i=0;i<5;i++)
        {
        	// 메소드 안 synchroized 이용 동기화
            synchronized (this) {
                System.out.println(Thread.currentThread().getName()+" 실행!"  );
                try {
                    Thread.sleep(100);

                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

 

 - 정적 메소드나 정적 변수는 클래스 자체의 락을 사용한다. 즉, 인스턴스 락과 클래스 락이 따로 존재한다.

   인스턴스가 3개 생성되었다면, 클래스 락을 포함해 총 4개의 락이 존재하는 것이다.

 

 - 스레드 우선순위는 존재하지만, 스레드 스케줄링을 보장할 수는 없다. 성능에 영향을 주기 위한 용도로만 사용하고, 프로그램이 제대로 작동되기 위한 용도로 우선순위를 사용하는 일은 절대 없어야 한다.

 

 

 

 

 

반응형

댓글