티스토리 뷰

728x90
반응형

Main Thread


  • 모든 자바 어플리케이션에서 메인 스레드는 다음과 같은 main 메서드를 통해서 실행하게 됩니다. 메인 메서드가 실행이 되면 코드는 한줄 한줄 순차적으로 시작하게 되고 return을 만나거나 main 메서드의 끝이오면 종료하게 됩니다.
  • 이런 main 메서드만 존재하는 상황을 싱글 스레드 어플리케이션이라고 하는데 main 스레드가 종료되면 프로그램 자체도 종료됩니다. main 스레드 구조에서 스레드를 여러개 생성하여 멀티 스레드로 구성할 수 있는데 그림으로 확인해보겠습니다.
  • main 스레드가 끝나고도 다른 스레드도 끝나야 프로세스가 종료가 되는데 데몬 스레드는 예외입니다.
public static void main(String[] args) {
    System.out.println("Main Thread 실행");
}

 

데몬 스레드


  • 데몬 스레드란 주 스레드의 작업을 돕는 보조적인 역할을 수행하는 스레드입니다. 주 스레드가 종료되면 데몬 스레드는 강제적으로 종료되는데 그 이유는 보조 역할을 수행하므로 주 스레드가 종료되면 데몬 스레드의 존재 의미가 없어지기 때문입니다.
  • 크롬이라는 메인 메서드가 실행이 되면서 네이버, 유튜브, 티스토리를 데몬 스레드라고 볼 수 있습니다.

 

데몬 스레드 설정 true
public class Example {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println("유튜브 영상 시청중");

                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {}
                }
            }
        });

        thread.setDaemon(true); // 데몬 스레드 설정
        thread.start();
        System.out.println("메인 메소드 종료");
    }
}

 

데몬 스레드 설정 false
public class Example {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println("유튜브 영상 시청중");

                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {}
                }
            }
        });

        thread.setDaemon(false); // 데몬 스레드 설정
        thread.start();
        System.out.println("메인 메소드 종료");
    }
}

 

스레드 동기화


  • 싱글 스레드 즉, main 스레드 한개로 구성된 어플리케이션에서는 문제가 되진 않지만 만약 멀티 스레드 상황에서 객체를 생성 한다면 여러개의 스레드가 객체를 건들 수 있는 상황이 생길 수 있습니다. 스레드 A를 사용하던 객체가 만약 스레드 B에 의해 객체의 값이 변경될 수 있다는 의미입니다.

잘못된 예제
public class Ash extends Thread{
    String item;

    public void setItem(String item) {
        this.item = item;

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {}

        System.out.println(Thread.currentThread().getName() + " - 현재 아이템 : " + this.item);
    }

    public String getItem() {
        return item;
    }
}

public class UserA extends Thread{
    private Ash ash;

    public void setAsh(Ash ash){
        this.setName("UserA");
        this.ash = ash;
    }

    @Override
    public void run() {
        ash.setItem("도란 방패");
    }
}

public class UserB extends Thread{
    private Ash ash;

    public void setAsh(Ash ash){
        this.setName("UserB");
        this.ash = ash;
    }

    @Override
    public void run() {
        ash.setItem("도란 검");
    }
}

public class Example {
    public static void main(String[] args) {
        System.out.println("소환사의 협곡에 오신걸 환영합니다.");
        Ash ash = new Ash();

        UserA userA = new UserA();
        userA.setAsh(ash);
        userA.start();

        UserB userB = new UserB();
        userB.setAsh(ash);
        userB.start();
    }
}

이러한 상황에서 지금 사용중인 객체(애쉬)를 다른 객체가 접근할 수 없도록 할려면 스레드가 작업이 끝날 때까지 객체에 잠금을 걸어서 다른 스레드가 접근을 할 수 없도록 해야합니다.

 

synchronized 키워드 사용
  • 임계 영역 - 멀티 스레드에서 하나의 스레드만 실행할 수 있는 코드 영역을 임계 영역이라고 합니다. 자바는 이러한 임계 영역을 지정하기 위해서 동기화 메서드와 동기화 블록을 제공합니다. 동기화 메서드를 만드는 방법은 메서드 선언에 synchronized 키워드를 선언하면 됩니다. 
public class Ash extends Thread{
    String item;

    public synchronized void setItem(String item) {
        this.item = item;

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {}

        System.out.println(Thread.currentThread().getName() + " - 현재 아이템 : " + this.item);
    }

    public String getItem() {
        return item;
    }
}

 

 

데드락


  • 스레드 1은 자원 1을 점유하고 있는 상태에서 자원 2가 필요합니다. 스레드 2는 자원 2를 점유하고 있는 상태에서 자원 1이 필요합니다. 하지만 스레드 1은 자원 2가 필요한 상태에서 자원 1을 빌려줄 수 있는 상황도 아니고 스레드 2또한 비슷한 상황입니다.

 

데몬 스레드 예제
public class ThreadA extends Thread{
    Object resource1;
    Object resource2;

    ThreadA(Object resource1, Object resource2){
        this.resource1 = resource1;
        this.resource2 = resource2;
    }


    @Override
    public void run() {
       synchronized (resource1) {
           System.out.println("스레드 1: 자원1 점유 중");

           try {
               Thread.sleep(500);
           } catch (InterruptedException e) {}

           System.out.println("스레드 1: 자원2 대기 중");

           synchronized (resource2) {
               System.out.println("스레드 1 : 자원1 & 2 점유 중");
           }
       }
    }
}

public class ThreadB extends Thread{
    Object resource1;
    Object resource2;

    ThreadB(Object resource1, Object resource2){
        this.resource1 = resource1;
        this.resource2 = resource2;
    }

    @Override
    public void run() {
        synchronized (resource2) {
            System.out.println("스레드 2: 자원2 점유 중");

            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {}

            System.out.println("스레드 2: 자원1 대기 중");

            synchronized (resource1) {
                System.out.println("스레드 2: 자원1 & 2 점유 중");
            }
        }
    }
}

public class Example {
    public static void main(String[] args) throws InterruptedException {
        Object resource1 = new Object();
        Object resource2 = new Object();

        ThreadA threadA = new ThreadA(resource1, resource2);
        ThreadB threadB = new ThreadB(resource1, resource2);

        threadA.start();
        threadB.start();

        Thread.sleep(1000);

        System.out.println(threadA.getState());
        System.out.println(threadB.getState());
    }
}

 

참고 사이트

https://catch-me-java.tistory.com/47

728x90
반응형

'JAVA > JAVA기본' 카테고리의 다른 글

JAVA - 어노테이션이란?  (0) 2022.01.29
JAVA - Enum이란?  (0) 2022.01.29
JAVA - Thread란?  (0) 2022.01.27
JAVA - Thread 클래스와 Runnable 인터페이스  (0) 2022.01.27
JAVA - 예외 처리(try, catch, throw, throws, finally)  (0) 2022.01.26