본문 바로가기
GD's IT Lectures : 기초부터 시리즈/자바(JAVA) 기초부터 ~

[자바(JAVA)] 자바의 동시성과 병렬 처리

by GDNGY 2023. 4. 30.
38.1. 동시성과 병렬 처리 개요

동시성(concurrency)은 여러 작업이 독립적으로 실행되는 것을 의미하며, 병렬 처리(parallelism)는 동시에 여러 작업이 실행되는 것을 의미합니다. 자바에서는 스레드를 사용하여 동시성과 병렬 처리를 구현할 수 있습니다. 이를 통해 성능이 향상되고 자원을 효율적으로 활용할 수 있습니다.

38.2. 쓰레드와 스레드 관리

자바에서 쓰레드를 생성하는 방법은 두 가지가 있습니다. 첫 번째 방법은 java.lang.Thread 클래스를 상속받는 방법이고, 두 번째 방법은 java.lang.Runnable 인터페이스를 구현하는 방법입니다.

 

  • Thread 클래스를 상속받는 방법 예제
class MyThread extends Thread {
    public void run() {
        System.out.println("MyThread is running.");
    }
}

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

 

  • Runnable 인터페이스를 구현하는 방법 예제
class MyRunnable implements Runnable {
    public void run() {
        System.out.println("MyRunnable is running.");
    }
}

public class RunnableExample {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
    }
}

 

38.3. 동기화와 락 (Lock)

동시성을 구현할 때 공유 자원에 대한 동시 접근으로 인한 문제가 발생할 수 있습니다. 이를 해결하기 위해 동기화(synchronization) 및 락(lock)을 사용할 수 있습니다.

 

동기화는 synchronized 키워드를 사용하여 구현할 수 있습니다. 

public class SynchronizedExample {
    private static int counter = 0;

    public static synchronized void incrementCounter() {
        counter++;
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                incrementCounter();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                incrementCounter();
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("Counter: " + counter);
    }
}

 

락(Lock)은 java.util.concurrent.locks 패키지의 Lock 인터페이스와 ReentrantLock 클래스를 사용하여 구현할 수 있습니다.

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockExample {
    private static int counter = 0;
    private static Lock lock = new ReentrantLock();

    public static void incrementCounter() {
        lock.lock();
        try {
            counter++;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                incrementCounter();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                incrementCounter();
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("Counter: " + counter);
    }
}

 

38.4. Executor 프레임워크와 ForkJoin 프레임워크

자바에서는 java.util.concurrent 패키지의 Executor 프레임워크를 사용하여 스레드 풀(thread pool)을 관리할 수 있습니다. 이를 통해 스레드 생성, 실행, 종료 등의 작업을 캡슐화하여 효율적으로 관리할 수 있습니다.

 

Executor 프레임워크

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        Runnable task1 = () -> System.out.println("Task 1 is running");
        Runnable task2 = () -> System.out.println("Task 2 is running");

        executorService.submit(task1);
        executorService.submit(task2);

        executorService.shutdown();
    }
}

 

ForkJoin 프레임워크는 병렬 처리를 위한 특별한 종류의 ExecutorService로, 작업을 여러 하위 작업으로 분할하여 병렬로 실행한 뒤 결과를 다시 합쳐 반환하는 과정을 지원합니다.


ForkJoin 프레임워크

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

public class ForkJoinExample {
    static class SumTask extends RecursiveTask<Long> {
        private int start;
        private int end;

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

        @Override
        protected Long compute() {
            if (end - start <= 10) {
                long sum = 0;
                for (int i = start; i <= end; i++) {
                    sum += i;
                }
                return sum;
            } else {
                int middle = (start + end) / 2;
                SumTask leftTask = new SumTask(start, middle);
                SumTask rightTask = new SumTask(middle + 1, end);

                leftTask.fork();
                rightTask.fork();

                return leftTask.join() + rightTask.join();
            }
        }
    }

    public static void main(String[] args) {
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        SumTask sumTask = new SumTask(1, 100);

        long result = forkJoinPool.invoke(sumTask);
        System.out.println("Sum: " + result);
    }
}

 

 

반응형

댓글