2021. 1. 22.

10주차 과제: 멀티쓰레드 프로그래밍

https://github.com/whiteship/live-study/issues/10

목표

자바의 멀티쓰레드 프로그래밍에 대해 학습하세요.

학습할 것 (필수)

  • Thread 클래스와 Runnable 인터페이스
  • 쓰레드의 상태
  • 쓰레드의 우선순위
  • Main 쓰레드
  • 동기화
  • 데드락


Thread 클래스와 Runnable 인터페이스

public class MyRunnable implements Runnable {

    @Override
    public void run() {
        System.out.println("Runnable thread");
    }

    public static void main(String args[]) {
        (new Thread(new MyRunnable())).start();
    }

}
public class MyThread extends Thread {

    public void run() {
        System.out.println("extends thread");
    }

    public static void main(String args[]) {
        (new MyThread()).start();
    }

}

출처: https://docs.oracle.com/javase/tutorial/essential/concurrency/runthread.html


쓰레드의 상태

NEW - 아직 시작되지 않은 스레드 상태

RUNNABLE - Java 가상 머신에서 실행중인 스레드 상태

BLOCKED - 모니터 잠금을 기다리면서 차단 된 스레드 상태

WAITING - 다른 스레드가 특정 작업을 수행 할 때까지 무기한 대기중인 스레드 상태

TIMED_WAITING - 다른 스레드가 지정된 대기 시간까지 작업을 수행하기를 기다리는 상태

TERMINATED - 종료 된 스레드 상태

public class MyThread implements Runnable {
    @Override
    public void run() {
        // moving thread2 to timed waiting state
        try {
            Thread.sleep(1500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("State of thread1 while it 
        called join() method on thread2 -"
        + MyTest.thread1.getState());
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class MyTest implements Runnable {
    public static Thread thread1;
    public static MyTest obj;

    public static void main(String[] args) {
        obj = new MyTest();
        thread1 = new Thread(obj);

        // thread1 created and is currently 
        // in the NEW state.
        System.out.println("State of thread1 after 
        creating it - " + thread1.getState());
        thread1.start();

        // thread1 moved to Runnable state
        System.out.println("State of thread1 after calling 
        .start() - " 
        + thread1.getState());
    }

    @Override
    public void run() {
        MyThread myThread = new MyThread();
        Thread thread2 = new Thread(myThread);

        // thread1 created and is currently in the NEW
        // state.
        System.out.println("State of thread2 after
        creating it - " + thread2.getState());
        
        thread2.start();

        // thread2 moved to Runnable state
        System.out.println("State of thread2 after calling
        .start() - " + thread2.getState());

        // moving thread1 to timed waiting state
        try {
            //moving thread1 to timed waiting state
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("State of thread2 after calling
        .sleep() - " + thread2.getState());

        try {
            // waiting for thread2 to die
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("State of thread2 when it has
        finished - " + thread2.getState());
    }
}
State of thread1 after creating it - NEW
State of thread1 after calling .start() - RUNNABLE
State of thread2 after creating it - NEW
State of thread2 after calling .start() - RUNNABLE
State of thread2 after calling .sleep() - TIMED_WAITING
State of thread1 while it called join() - WAITING
State of thread2 when it has finished - TERMINATED

Process finished with exit code 0

코드 출처: https://www.geeksforgeeks.org/lifecycle-and-states-of-a-thread-in-java/


쓰레드의 우선순위

public static int MIN_PRIORITY: 최소 우선 순위. 값: 1

public static int NORM_PRIORITY: 기본 우선 순위. 값: 5. 명시하지 않을 경우 기본값.

public static int MAX_PRIORITY: 스레드의 최대 우선 순위. 값: 10


public final int getPriority(): java.lang.Thread.getPriority() 주어진 스레드의 우선 순위를 리턴

public final void setPriority(int newPriority): java.lang.Thread.setPriority() 스레드의 우선 순위를 newPriority 값으로 변경. newPriority의 값이 minimum(1) maximum(10) 한계를 초과하면이 IllegalArgumentException 발생

public class ThreadDemo extends Thread {

    public void run() {
        System.out.println("Inside run method");
    }

    public static void main(String[] args) {
        ThreadDemo t1 = new ThreadDemo();
        ThreadDemo t2 = new ThreadDemo();
        ThreadDemo t3 = new ThreadDemo();

        // Default 5
        System.out.println("t1 thread priority : "
                + t1.getPriority());

        // Default 5
        System.out.println("t2 thread priority : "
                + t2.getPriority());

        // Default 5
        System.out.println("t3 thread priority : "
                + t3.getPriority());

        t1.setPriority(2);
        t2.setPriority(5);
        t3.setPriority(8);

        // t3.setPriority(21); will throw
        // IllegalArgumentException

        // 2
        System.out.println("t1 thread priority : "
                + t1.getPriority());

        // 5
        System.out.println("t2 thread priority : "
                + t2.getPriority());

        // 8
        System.out.println("t3 thread priority : "
                + t3.getPriority());

        // Main thread

        // Displays the name of
        // currently executing Thread
        System.out.println(
                "Currently Executing Thread : "
                + Thread.currentThread().getName());

        System.out.println(
                "Main thread priority : "
                + Thread.currentThread().getPriority());

        // Main thread priority is set to 10
        Thread.currentThread().setPriority(10);
        System.out.println(
                "Main thread priority : "
                + Thread.currentThread().getPriority());
    }
}
t1 thread priority : 5
t2 thread priority : 5
t3 thread priority : 5
t1 thread priority : 2
t2 thread priority : 5
t3 thread priority : 8
Currently Executing Thread : main
Main thread priority : 5
Main thread priority : 10

Process finished with exit code 0

코드 출처: https://www.geeksforgeeks.org/java-thread-priority-multithreading/


Main 쓰레드

Java 프로그램이 시작되면 하나의 스레드가 즉시 실행. 프로그램이 시작될 때 실행되는 스레드이기 때문에 일반적으로 프로그램의 메인 스레드라고 한다.

"자식"스레드가 생성되는 스레드

public class MyTestThread extends Thread {

    public static void main(String[] args) {
        // getting reference to Main thread
        Thread t = Thread.currentThread();
        // getting name of Main thread
        System.out.println(
                "Current thread: "
                        + t.getName());
        // changing the name of Main thread
        t.setName("Geeks");
        System.out.println(
                "After name change: "
                        + t.getName());
        // getting priority of Main thread
        System.out.println(
                "Main thread priority: "
                        + t.getPriority());
        // setting priority of Main thread to MAX(10)
        t.setPriority(MAX_PRIORITY);
        System.out.println(
                "Main thread new priority: "
                        + t.getPriority());
    }
}
Current thread: main
After name change: Geeks
Main thread priority: 5
Main thread new priority: 10

Process finished with exit code 0

코드 출처: https://www.geeksforgeeks.org/main-thread-java/


동기화

public class SynchronizedCounter {
    private int c = 0;

    public synchronized void increment() {
        c++;
    }

    public synchronized void decrement() {
        c--;
    }

    public synchronized int value() {
        return c;
    }
}

1. 동기화(synchronized) 메소드를 동일한 객체에서 두 번 호출하는 인터리빙(interleave - 스레드가 수행되는 순서)이 불가능해진다. 첫 스레드가 동기화 메소드를 실행 완료할 때까지 같은 블록의 동기화 메소드를 실행한 모든 스레드는 실행을 일시 중단하고 기다린다.

2. 동기화 메소드가 종료되면 같은 객체의 동기화된 메소드의 다음 호출 관계를 자동으로 설정. 모든 스레드에 객체의 상태 변경 사항을 보여준다.

생성자는 동기화가 불가능. 생성자를 동기화하는 것은 의미가 없습니다. 객체를 생성하는 스레드만이 접근할 수 있어야 하기 때문.

출처: https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html


데드락

public class Deadlock {
    static class Friend {
        private final String name;
        public Friend(String name) {
            this.name = name;
        }
        public String getName() {
            return this.name;
        }
        public synchronized void bow(Friend bower) {
            System.out.format("%s: %s"
                + "  has bowed to me!%n", 
                this.name, bower.getName());
            
            // 교착상태를 만드는 부분
            bower.bowBack(this);
        }
        public synchronized void bowBack(Friend bower) {
            System.out.format("%s: %s"
                + " has bowed back to me!%n",
                this.name, bower.getName());
        }
    }

    public static void main(String[] args) {
        final Friend alphonse = new Friend("Alphonse");
        final Friend gaston = new Friend("Gaston");
        
        new Thread(new Runnable() {
            public void run() { alphonse.bow(gaston); }
        }).start();
        
        new Thread(new Runnable() {
            public void run() { gaston.bow(alphonse); }
        }).start();
    }
}

코드 출처: https://docs.oracle.com/javase/tutorial/essential/concurrency/deadlock.html

동기화된 bowBack 메소드를 호출하지만 동기화된 bow 메소드 종료를 기다리기에 교착상태(Deadlock) 발생


댓글 없음:

댓글 쓰기