스레드 구현

김승규·2024년 1월 6일

스레드를 구현 하는 방법은 3가지 있다.
1. Thread 클래스를 상속받는 방법
2. Runnable 인터페이스를 구현하는 방법
3. Runnable 인터페이스를 구현하고 Thread 클래스를 사용하는 방법

Thread 구현 방법 비교

  • Thread class를 상속한 thread : 관련 메소드를 편하게 사용가능하다.
  • Runnable interface를 구현한 thread : 상속에 비해 가볍고, 다른 클래스를 상속받을 수 있다.
  • Self Runnable Thread : 위에 두 가지 방식을 합친 방식으로, Runnable을 구현하고 Thread를 사용한다.

Threard를 사용할 때 알면 좋은 키워드들

  • synchronized : 메소드는 아니지만, 접근 제한자로 동시성 문제를 해결할 수 있다.
  • join() : 해당 스레드가 종료될 때까지 기다린다.
  • Thread Pool : 여러 개의 스레드를 미리 생성해놓고 재사용하는 그룹으로 스레드 생성 및 삭제에 따른 시간과 자원을 절약할 수 있다.
  • setDemon(true) : 해당 스레드를 데몬 스레드(다른 모든 유저 스레드가 종료되면 자동 종료되는 스레드)로 할당한다.

Counter 클래스

먼저 Counter 클래스를 만든다. Counter 클래스는 1초마다 횟수를 1씩 증가시킨다.

public class Counter {
    String name;
    int count = 0;
    int maxCount = 0;

    public Counter(String name, int maxCount) {
        this.name = name;
        this.maxCount = maxCount;
    }

    public void run() {
        while(count < maxCount) {
            ++count;
            System.out.println(name + " : " + count);
            try {
                Thread.sleep(1000);
            } catch(InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    public static void main(String[] args) {
        Counter counter1 = new Counter("counter1", 5);
        Counter counter2 = new Counter("counter2", 10);
        counter1.run();
        counter2.run();
        System.out.println("Main Thread");
    }
}

Counter의 객체를 2개 만들어서 각자 run을 돌리면 순서대로 실행되게 된다.

Thread를 상속한 ThreadCounter 클래스

public class ThreadCounter extends Thread {
    String name;
    int count = 0;
    int maxCount = 0;

    public ThreadCounter(String name, int maxCount) {
        this.name = name;
        this.maxCount = maxCount;
    }

    @Override
    public void run() {
        while(count < maxCount) {
            ++count;
            System.out.println(name + " : " + count);
            try {
                Thread.sleep(1000);
            } catch(InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadCounter counter1 = new ThreadCounter("counter1", 5);
        ThreadCounter counter2 = new ThreadCounter("counter2", 10);
        counter1.start();
        counter2.start();
//        counter1.join();
        System.out.println("Main Thread");
    }
}

Runnable을 구현한 RunnableCounter 클래스

public class RunnableCounter implements Runnable {
    String name;
    int count = 0;
    int limit;

    public RunnableCounter(String name, int limit) {
        this.name = name;
        this.limit = limit;
    }

    public void stop() {
        Thread.currentThread().interrupt();
    }

    @Override
    public void run() {
        while(count < limit) {
            ++count;
            System.out.println(name + " : " + count);
            try {
                Thread.sleep(1000);
            } catch(InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        RunnableCounter counter1 = new RunnableCounter("counter1", 10);
        RunnableCounter counter2 = new RunnableCounter("counter2", 10);
        int limit = 10;
        String name = "counter3";

        Thread thread = new Thread(() -> {
            int count = 0;
            while(count < limit) {
                ++count;
                System.out.println(name + " : " + count);
                try {
                    Thread.sleep(1000);
                } catch(InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        });

        ExecutorService pool = Executors.newFixedThreadPool(2);

        pool.execute(counter1);
        pool.execute(counter2);
        pool.execute(thread);

        pool.shutdown();

//        RunnableCounter counter = new RunnableCounter("counter1", 10);
//        Thread thread1 = new Thread(counter);
//        Thread thread2 = new Thread(counter);
//        Thread thread3 = new Thread(counter);
//        thread1.start();
//        thread2.start();
//        thread3.start();
    }
}

Runnable를 구현하고 Thread를 상속한 SelfRunnableCounter

public class SelfRunnableCounter implements Runnable {
    static int commonCount = 0;
    String name;
    int count = 0;
    int maxCount;
    Thread thread;

    public SelfRunnableCounter(String name, int maxCount) {
        thread = new Thread(this);
        this.name = name;
        this.maxCount = maxCount;
    }

    public void start() {
        thread.start();
    }

    public void stop() {        // pool 사용 불가?
        thread.interrupt();
    }

    public static synchronized void incrementCommonCount() {
        ++commonCount;
    }

//    public void incrementCommonCount() {
//        synchronized(this) {
//            ++commonCount;
//        }
//    }

    @Override
    public void run() {
        while(!Thread.currentThread().isInterrupted() && (count < maxCount)) {
            ++count;
            incrementCommonCount();
//            ++commonCount;
            System.out.println(name + " : " + count + ", " + commonCount);
            try {
                Thread.sleep(100);
            } catch(InterruptedException e) {
                System.out.println("InterruptedException");
                Thread.currentThread().interrupt();
            }
        }
        System.out.println("Stop Thread");
    }

    public static void main(String[] args) throws InterruptedException {
        SelfRunnableCounter counter1 = new SelfRunnableCounter("counter1", 100);
        SelfRunnableCounter counter2 = new SelfRunnableCounter("counter2", 100);
        ExecutorService pool = Executors.newFixedThreadPool(2);

        pool.execute(counter1);
        pool.execute(counter2);

        pool.shutdown();

//        counter1.start();
//        counter2.start();

//        counter1.start();
//        Thread.sleep(5000);
//        counter1.stop();
    }
}
profile
꿈꾸는 리얼리스트 개발자 김승규입니다.

0개의 댓글