2022/02/26 자바의 다양한 기능들

김석진·2022년 2월 26일
0

자바에서 Thread 만들기

Thread란?

  • process: (프로그램이 메모리를 차지한 상태) 실행 중인 프로그램 프로그램이 실행되면 OS로 부터 메모리를 할당받아 프로세스 상태가 됨
  • thread: 하나의 프로세스는 하나 이상의 thread를 가지게 되고, 실제 작업을 수행하는 단위는 thread임

multi-threading

  • 여러 thread가 동시에 수행되는 프로그래밍, 여러 작업이 동시에 실행되는 효과
  • thread는 각각 자신만의 작업 공간을 가짐(context)
  • 각 thread 사이에서 공유하는 자원이 있을 수 있음(자바에서는 static instance)
  • 여러 thread가 자원을 공유하여 작업이 수행되는 경우 서로 자원을 차지하려는 race condition이 발생할 수 있음
  • 이렇게 여러 thread가 공유하는 자원중 경쟁이 발생하는 부분을 critical section이라고 함
  • critical section에 대한 동기화(일종의 순차적 수행)를 구현하지 않으면 오류가 발생할 수 있음

Thread Status


Thread가 Start되면 Thread가 ThreadPool에 들어오게 되는데 Runnable한 상태에 들어서게 된다. Thread가 종료되면 Dead됐다라고 함
Not Runnable상태는 CPU를 절대 점유할 수 없는 상태 여기에 계속 갈 수 있는 경우가 Java에서는 3가지 메서드일때 하나는 sleep(시간), wait()은 리소스가 한정,여러쓰레드가 있을때 유효한 Thread가 있을 때 까지 기다리게된다-> Not runnable상태로됨 리소스가 유효한 상태가 될때 프로그램에서 notify()를 실행하면 쓰레드 중 하나만 실행상태(Runnable)로 된다.(notifyall()은 전부다) join()을 다른 스레드에 걸면 자기자신은 Not Runnable상태로 빠지고 다른스레드가 exits가 되면 자기자신은 Runnable로 바뀐다.

Thread 클래스의 여러 메서드들

Thread 우선순위

  • Thread.MIN_PRIORITY(=1)~Thread.MAX_PRIORITY(=10)
  • 디폴트 우선순위 : Thread.NORMAL_PRIORITY(=5)
  • 우선 순위가 높은 Thread가 CPU의 배분을 받을 확률이 높다(확률 이 높다는거지 먼저 실행되는것은 아님)
  • setPriority()/getPriority()

join()

  • 동시에 두 개 이상의 Thread가 실행 될때 다른 Thread의 결과를 참조하여 실행해야 하는 경우 join() 함수를 사용
  • join() 함수를 호출한 Thread가 not-runnable 상태가 감
  • 다른 Thread의 수행이 끝나면 runnable 상태로 돌아옴

  • 1부터 50,51부터 100까지의 합을 구하는 두 개의 Thread를 만들어 그 결과를 확인
public class JoinTest extends Thread {

    int start;
    int end;
    int total;

    public JoinTest(int start, int end) {
        this.start = start;
        this.end = end;

    }

    public void run() {

        int i;
        for (i = start; i <= end; i++) {
            total += i;
        }
    }

    public static void main(String[] args) {

        JoinTest jt1 = new JoinTest(1, 50);
        JoinTest jt2 = new JoinTest(51, 100);

        jt1.start();
        jt2.start();

        try {
            jt1.join();
            jt2.join();

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

        int lastTotal = jt1.total + jt2.total;
        System.out.println("jt1.total = " + jt1.total);
        System.out.println("jt2.total= " + jt2.total);
        System.out.println("합계 : " + lastTotal);


    }

}

interrupt()

  • 다른 Thread에 예외를 발생시키는 interrupt를 보낸다
  • Thread가 join(),sleep(),wait() 함수에 의해 not-runnable 상태일 때 iterrupt() 메서드를 호출하면 다시 runnable 상태가 될 수 있음

Thread 종료하기

  • Thread를 종료할 때 사용함
  • 무한 반복의 경우 while(flag)의 flag변수값을 false로 바꾸어 종료시킴
  • Thread 종료하기 예제

멀티 Thread 프로그래밍에서의 동기화

critical section과 semaphore

  • critical section은 두 개 이상의 thread가 동시에 접근 하는 경우 문제가 생길 수 있기 때문에 동시에 접근할 수 없는 영역
  • semaphore 는 특별한 형태의 시스템 객체이며 get/release 두 개의 기능이 있다.
  • 한 순간 오직 하나의 thread만이 semaphore를 얻을 수 있고, 나머지 thread들은 대기(blocking)상태가 됨
  • semaphore를 얻은 thread 만이 critical section에 들어갈 수 있다.
  • Park과 ParkWife가 동시에 Bank 자원에 접근하여 작업이 이루어지는 경우의 예제
class Bank {
    private int money = 10000;

    public void saveMoney(int save) {

        int m = getMoney();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        setMoney(m + save);

    }

    public void minusMoney(int minus) {

        int m = getMoney();

        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        setMoney(m - minus);
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }
}

class Park extends Thread {

    public void run() {
        System.out.println("start save");
        SyncMain.myBank.saveMoney(3000);
        System.out.println("saveMoney(3000) :   " + SyncMain.myBank.getMoney());
    }
}

class ParkWife extends Thread {

    public void run() {
        System.out.println("start minus");
        SyncMain.myBank.minusMoney(1000);
        System.out.println("minusMoney(1000) :   " + SyncMain.myBank.getMoney());
    }
}


public class SyncMain {

    public static Bank myBank = new Bank();

    public static void main(String[] args) throws InterruptedException {

        Park p = new Park();
        p.start();

        Thread.sleep(200);

        ParkWife pw = new ParkWife();
        pw.start();


    }
}

이렇게 실행을 한다면

이런식이다 쓰레드가 두개인경우 공유 자원을 사용하고있는데 하나의 쓰레드가 공유 자원을 사용할 때 다른 쓰레드가 공유 자원을 사용할때 공유자원이 변경이 이뤄지면 안된다.

변경한 코드

class Bank {
    private int money = 10000;

    public synchronized void saveMoney(int save) {

        int m = getMoney();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        setMoney(m + save);

    }

    public synchronized void minusMoney(int minus) {

        int m = getMoney();

        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        setMoney(m - minus);
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }
}

class Park extends Thread {

    public void run() {
        System.out.println("start save");
        SyncMain.myBank.saveMoney(3000);
        System.out.println("saveMoney(3000) :   " + SyncMain.myBank.getMoney());
    }
}

class ParkWife extends Thread {

    public void run() {
        System.out.println("start minus");
        SyncMain.myBank.minusMoney(1000);
        System.out.println("minusMoney(1000) :   " + SyncMain.myBank.getMoney());
    }
}


public class SyncMain {

    public static Bank myBank = new Bank();

    public static void main(String[] args) throws InterruptedException {

        Park p = new Park();
        p.start();

        Thread.sleep(200);

        ParkWife pw = new ParkWife();
        pw.start();


    }
}

동기화(synchronization)

  • 두 개의 thread가 같은 객체에 접근 할 경우, 동시에 접근 함으로써 오류가 발생
  • 동기화는 임계영역에 접근한 경우 공유자원을 lock 하여 다른 thread의 접근을 제어
  • 동기화를 잘못 구현하면 deadlock에 빠질 수 있다는 문제점이 있다.

자바에서는 synchronized 메서드나 synchronized 블럭을 사용

synchronized 블럭

  • 현재 객체는 또는 다른 객체를 lock으로 만든다.
synchronized(참조형 수식) {

      수행문;
}

synchronized 메서드

  • 객체의 메소드에 synchronized 키워드 사용
  • 현재 이 메서드가 속해있는 객체이 lock을 건다
  • 자바에서는 deadlock을 방지하는 기술이 제공되지 않으므로 synchronized 메서드에서 다른 synchronized 메서드는 호출하지 않도록 하한다
  • deadlock의 예

wait()/notify() 메서드를 활용한 동기화 프로그래밍

  • 리소스가 어떤 조건에서 더 이상 유효하지 않은 경우 리소스를 기다리기 위해 Thread가 wait() 상태가 된다.
  • wait() 상태가 된 Thread은 notify()가 호출 될 때까지 기다린다.
  • 유효한 자원이 생기면 notify()가 호출되고 wait()하고 있는 Thread 중 무작위로 하나 Thread를 재시작 하도록 한다.
  • notifyAll()이 호출되는 경우 wait()하고 있는 모든 Thread가 재시작 된다.
  • 이 경우 유효한 리소스만큼의 Thread만이 수행될 수 있고 자원을 갖지 못한 Thread의 경우는 다시 wait() 상태로 만든다
  • 자바에서는 notifyAll()메서드의 사용을 권장함
  • 도서관에서 책을 빌리는 예제
profile
주니어 개발자 되고싶어요

0개의 댓글