2023. 6. 6. 06:59ㆍHigh Level Programming Language/Essential Java Classes
High Level Concurrency Objects
지금까지 이 레슨에서는 처음부터 Java 플랫폼의 일부였던 저수준 API에 중점을 두었습니다. 이러한 API는 매우 기본적인 작업에 적합하지만 고급 작업에는 더 높은 수준의 빌딩 블록이 필요합니다. 오늘날의 다중 프로세서 및 다중 코어 시스템을 완전히 활용하는 대규모 동시(concurrent) 응용 프로그램의 경우 특히 그렇습니다.
이 섹션에서는 Java 플랫폼 버전 5.0에 도입된 고급 동시성(high-level concurrency) 기능 중 일부를 살펴보겠습니다. 이러한 기능의 대부분은 java.util.concurrent 패키지에서 구현됩니다. Java Collections Framework에는 동시(concurrent) 데이터 구조도 있습니다.
- Lock objects는 많은 동시 응용 프로그램을 단순화하는 locking 관용구를 지원합니다..
- Executors는 스레드를 시작하고 관리하기 위한 상위 수준 API를 정의합니다. java.util.concurrent에서 제공하는 Executor 구현은 대규모 애플리케이션에 적합한 스레드 풀(pool) 관리를 제공합니다.
- Concurrent collections을 통해 대규모 데이터 수집을 보다 쉽게 관리할 수 있으며 동기화(synchronization)의 필요성을 크게 줄일 수 있습니다.
- Atomic variables에는 동기화를 최소화하고 메모리 일관성 오류를 방지하는 기능이 있습니다.
- ThreadLocalRandom은 여러 스레드에서 의사 난(pseudo-random)수를 효율적으로 생성합니다.
Lock Objects
동기화된 코드(Synchronized code)는 간단한 종류의 재진입(reentrant) 락에 의존합니다. 이러한 종류의 락은 사용하기 쉽지만 많은 제한이 있습니다. 보다 정교한 locking 관용구는 java.util.concurrent.locks 패키지에서 지원됩니다. 이 패키지를 자세히 검토하지는 않지만, 대신 가장 기본적인 인터페이스인 Lock에 중점을 둘 것입니다.
락 객체(Lock objects)는 동기화된 코드에서 사용하는 암시적 락과 매우 유사하게 작동합니다. 암시적 락과 마찬가지로 한 번에 하나의 스레드만 Lock 객체를 소유할 수 있습니다. Lock 객체는 관련 Condition 객체를 통해 wait/notify 메커니즘도 지원합니다.
암시적 락에 비해 Lock 객체의 가장 큰 장점은 락을 획득하려는 시도를 취소할 수 있다는 것입니다. 락을 즉시 사용할 수 없거나 제한 시간(timeout)이 만료되기 전에(지정된 경우) tryLock 메서드(락을 획득하려는 시도)는 취소(back out)됩니다. 락을 획득하기 전에 다른 스레드가 인터럽트를 보내면 lockInterruptibly 메서드가 취소됩니다.
Lock 객체를 사용하여 Liveness에서 본 교착 상태(deadlock) 문제를 해결해 보겠습니다. Alphonse와 Gaston은 친구가 언제 인사를 하려고 하는지 알아차리도록 스스로 훈련했습니다. 우리는 Friend 객체가 인사를 진행하기 전에 두 참가자 모두에 대한 락을 획득하도록 요구함으로써 이 개선 사항을 모델링합니다. 다음은 개선된 모델인 Safelock의 소스 코드입니다. 이 관용구의 다재다능함을 보여주기 위해 우리는 Alphonse와 Gaston이 서로에게 인사하는 것을 멈출 수 없을 정도로 안전하게 인사를 할 수 있는 새로운 능력에 너무 매료되었다고 가정합니다.
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.Random;
public class Safelock {
static class Friend {
private final String name;
private final Lock lock = new ReentrantLock();
public Friend(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public boolean impendingBow(Friend bower) {
Boolean myLock = false;
Boolean yourLock = false;
try {
myLock = lock.tryLock();
yourLock = bower.lock.tryLock();
} finally {
if (! (myLock && yourLock)) {
if (myLock) {
lock.unlock();
}
if (yourLock) {
bower.lock.unlock();
}
}
}
return myLock && yourLock;
}
public void bow(Friend bower) {
if (impendingBow(bower)) {
try {
System.out.format("%s: %s has"
+ " bowed to me!%n",
this.name, bower.getName());
bower.bowBack(this);
} finally {
lock.unlock();
bower.lock.unlock();
}
} else {
System.out.format("%s: %s started"
+ " to bow to me, but saw that"
+ " I was already bowing to"
+ " him.%n",
this.name, bower.getName());
}
}
public void bowBack(Friend bower) {
System.out.format("%s: %s has" +
" bowed back to me!%n",
this.name, bower.getName());
}
}
static class BowLoop implements Runnable {
private Friend bower;
private Friend bowee;
public BowLoop(Friend bower, Friend bowee) {
this.bower = bower;
this.bowee = bowee;
}
public void run() {
Random random = new Random();
for (;;) {
try {
Thread.sleep(random.nextInt(10));
} catch (InterruptedException e) {}
bowee.bow(bower);
}
}
}
public static void main(String[] args) {
final Friend alphonse =
new Friend("Alphonse");
final Friend gaston =
new Friend("Gaston");
new Thread(new BowLoop(alphonse, gaston)).start();
new Thread(new BowLoop(gaston, alphonse)).start();
}
}
Executors
앞의 모든 예에서 Runnable 객체에 의해 정의된 대로 새로운 스레드가 수행하는 작업과 Thread 객체에 의해 정의된 스레드 자체 사이에는 긴밀한 연결(close connection)이 있습니다. 이는 소규모 응용 프로그램에서는 잘 작동하지만 대규모 응용 프로그램에서는 스레드 관리 및 생성을 응용 프로그램의 다른 영역으로 분리하는 것이 좋습니다. 이러한 기능을 캡슐화하는 객체를 executors라고 합니다. 다음 하위 섹션에서는 executors에 대해 자세히 설명합니다.
- Executor Interfaces define the three executor object types.
- Thread Pools are the most common kind of executor implementation.
- Fork/Join 다중 프로세서를 활용하기 위한 프레임워크(JDK 7의 새로운 기능)입니다.
Executor Interfaces
The java.util.concurrent package defines three executor interfaces:
- Executor, a simple interface that supports launching new tasks.
- ExecutorService는 Executor의 하위 인터페이스로, 개별 tasks과 executor 자체의 life cycle를 관리하는 데 도움이 되는 기능을 추가합니다.
- ScheduledExecutorService, ExecutorService의 하위 인터페이스인 ScheduledExecutorService는 향후 및/또는 정기적인 tasks 실행을 지원합니다.
일반적으로 executor 객체를 참조하는 변수는 executor 클래스 타입이 아닌 이 세 가지 인터페이스 타입 중 하나로 선언됩니다.
The Executor Interface
Executor 인터페이스는 일반적인 스레드 생성 관용구를 대체하도록 설계된 단일 메서드인 execute를 제공합니다.
r이 Runnable 객체이고 e가 Executor 객체인 경우
(new Thread(r)).start();
위 관용구를 아래 코드로 바꿀 수 있습니다.
e.execute(r);
그러나 execute의 정의는 덜 구체적(specific)입니다. 저수준 관용구(low-level idiom)는 새로운 스레드를 만들고 즉시 시작합니다. Executor 구현에 따라 execute는 동일한 작업을 수행할 수 있지만 기존 worker thread를 사용하여 r(Runnable)을 실행하거나 r을 큐에 배치하여 worker thread가 사용 가능해질 때까지 기다릴 가능성이 더 큽니다. (worker thread는 Thread Pools 섹션에서 설명합니다.)
java.util.concurrent의 executor 구현은 기본 Executor 인터페이스에서도 동작하지만 고급 ExecutorService 및 ScheduledExecutorService 인터페이스를 최대한 활용하도록 설계되었습니다.
The ExecutorService Interface
ExecutorService 인터페이스는 Executor 인터페이스와 유사하지만 더 다양한 submit method으로 execute을 보완합니다. execute와 마찬가지로 submit은 Runnable 객체를 허용하지만 task가 값을 반환할 수 있도록 하는 Callable 객체도 허용합니다. submit 메서드는 Callable 리턴 값을 검색하고 Callable 및 Runnable tasks의 상태를 관리하는 데 사용되는 Future 객체를 리턴합니다.
ExecutorService는 Callable 객체의 대규모 collections을 submit하기 위한 메서드도 제공합니다. 마지막으로 ExecutorService는 executor 종료를 관리하기 위한 여러 메서드를 제공합니다. 즉각적인 종료를 지원하려면 tasks가 interrupts를 올바르게 처리해야 합니다.
The ScheduledExecutorService Interface
ScheduledExecutorService 인터페이스는 지정된 지연(delay) 후에 Runnable 또는 Callable task를 실행하는 일정으로 부모 ExecutorService의 메서드를 보완합니다. 또한 인터페이스는 지정된 tasks을 정의된 시간 간격으로 반복적으로 실행하는 scheduleAtFixedRate 및 scheduleWithFixedDelay를 정의합니다.
Thread Pools
java.util.concurrent의 대부분의 executor 구현은 worker threads로 구성된 thread pools을 사용합니다. 이러한 종류의 스레드는, 자신이 실행시키는 Runnable 및 Callable tasks과 별도로 존재하며 종종 여러 tasks을 실행하는 데 사용됩니다.
Using worker threads minimizes the overhead due to thread creation. Thread objects use a significant amount of memory, and in a large-scale application, allocating and deallocating many thread objects creates a significant memory management overhead.
worker threads를 사용하면 스레드 생성으로 인한 오버헤드가 최소화됩니다. Thread 객체는 상당한 양의 메모리를 사용하며 대규모 응용 프로그램에서 많은 스레드 객체를 할당 및 할당 해제하면 상당한 메모리 관리 오버헤드가 발생합니다.
스레드 풀의 일반적인 유형 중 하나는 고정(fixed) 스레드 풀입니다. 이 유형의 풀에는 항상 지정된 수의 스레드가 실행 중입니다. 스레드가 아직 사용 중인 상태에서 어떤 이유로 종료된다면, 자동으로 새로운 스레드로 대체됩니다. Tasks은 스레드보다 active tasks이 많을 때마다 추가 작업을 보유하는 내부 대기열(internal queue)을 통해 풀에 제출됩니다.
고정 스레드 풀의 중요한 이점은 이를 사용하는 응용 프로그램에게 우아한 성능 저하를 보장한다는 것입니다. 이를 이해하기 위해 각 HTTP 요청이 별도의 스레드에서 처리되는 웹 서버 애플리케이션을 고려해보세요. 응용 프로그램이 각 새로운 HTTP 요청마다 새 스레드를 생성하고, 시스템이 즉시 처리할 수 있는 것보다 더 많은 요청을 받는 경우 모든 스레드들의 오버헤드가 시스템 용량을 초과할 때 응용 프로그램은 갑자기 모든 요청에 대한 응답을 중지합니다. 스레드 생성 수에 제한을 두면, 애플리케이션은 요청이 도착하는 속도만큼 서비스하지는 않지만, 시스템이 지속 가능한 속도로 요청을 처리할 수 있습니다.
고정 스레드 풀을 사용하는 executor를 만드는 간단한 방법은 java.util.concurrent.Executors에서 newFixedThreadPool 팩토리 메서드를 호출하는 것입니다. 이 클래스는 다음과 같은 팩토리 메서드도 제공합니다.
- The newCachedThreadPool method creates an executor with an expandable thread pool. This executor is suitable for applications that launch many short-lived tasks.
- The newSingleThreadExecutor method creates an executor that executes a single task at a time.
- Several factory methods are ScheduledExecutorService versions of the above executors.
If none of the executors provided by the above factory methods meet your needs, constructing instances of java.util.concurrent.ThreadPoolExecutor or java.util.concurrent.ScheduledThreadPoolExecutor will give you additional options.
위의 팩토리 메서드에서 제공하는 executors가 사용자의 요구를 충족하지 않는 경우, java.util.concurrent.ThreadPoolExecutor 또는 java.util.concurrent.ScheduledThreadPoolExecutor 인스턴스를 구성하면 추가 옵션이 제공됩니다.
Fork/Join
The fork/join framework is an implementation of the ExecutorService interface that helps you take advantage of multiple processors. It is designed for work that can be broken into smaller pieces recursively. The goal is to use all the available processing power to enhance the performance of your application.
fork/join 프레임워크는 여러 프로세서를 활용하는 데 도움이 되는 ExecutorService 인터페이스의 구현입니다. 재귀적으로 더 작은 조각으로 나눌 수 있는 작업을 위해 설계되었습니다. 목표는 사용 가능한 모든 처리 능력을 사용하여 애플리케이션의 성능을 향상시키는 것입니다.
As with any ExecutorService implementation, the fork/join framework distributes tasks to worker threads in a thread pool. The fork/join framework is distinct because it uses a work-stealing algorithm. Worker threads that run out of things to do can steal tasks from other threads that are still busy.
모든 ExecutorService 구현과 마찬가지로 fork/join 프레임워크는 작업을 스레드 풀의 worker threads에 배포합니다. fork/join 프레임워크는 work-stealing 알고리즘을 사용하기 때문에 구별됩니다. 할 일이 부족한 작업자 스레드는 여전히 사용 중인 다른 스레드에서 작업을 훔칠 수 있습니다.
fork/join 프레임워크의 중심은 AbstractExecutorService 클래스의 확장인 ForkJoinPool 클래스입니다. ForkJoinPool은 핵심 work-stealing 알고리즘을 구현하고 ForkJoinTask 프로세스를 실행할 수 있습니다.
Basic Use
The first step for using the fork/join framework is to write code that performs a segment of the work. Your code should look similar to the following pseudocode:
fork/join 프레임워크를 사용하기 위한 첫 번째 단계는 작업의 세그먼트를 수행하는 코드를 작성하는 것입니다. 코드는 다음 의사 코드(pseudocode)와 유사해야 합니다.
if (my portion of the work is small enough)
do the work directly
else
split my work into two pieces
invoke the two pieces and wait for the results
Wrap this code in a ForkJoinTask subclass, typically using one of its more specialized types, either RecursiveTask (which can return a result) or RecursiveAction.
일반적으로 RecursiveTask(결과를 리턴할 수 있음) 또는 RecursiveAction 중 하나를 사용하여 ForkJoinTask 하위 클래스에 이 코드를 래핑합니다.
After your ForkJoinTask subclass is ready, create the object that represents all the work to be done and pass it to the invoke() method of a ForkJoinPool instance.
ForkJoinTask 하위 클래스가 준비되면 수행할 모든 작업을 나타내는 객체를 만들고 ForkJoinPool 인스턴스의 invoke() 메서드에 전달합니다.
Blurring for Clarity
To help you understand how the fork/join framework works, consider the following example. Suppose that you want to blur an image. The original source image is represented by an array of integers, where each integer contains the color values for a single pixel. The blurred destination image is also represented by an integer array with the same size as the source.
fork/join 프레임워크의 작동 방식을 이해하는 데 도움이 되도록 다음 예제를 고려하십시오. 이미지를 흐리게 하고 싶다고 가정합니다. 원본 소스 이미지는 정수 배열로 표시되며 각 정수에는 단일 픽셀의 색상 값이 포함됩니다. 흐리게 처리된 대상 이미지는 소스와 동일한 크기의 정수 배열로도 표현됩니다.
Performing the blur is accomplished by working through the source array one pixel at a time. Each pixel is averaged with its surrounding pixels (the red, green, and blue components are averaged), and the result is placed in the destination array. Since an image is a large array, this process can take a long time. You can take advantage of concurrent processing on multiprocessor systems by implementing the algorithm using the fork/join framework. Here is one possible implementation:
blur를 수행하는 것은 소스 배열을 통해 한 번에 한 픽셀씩 작업하여 수행됩니다. 각 픽셀은 주변 픽셀로 평균화되고(빨간색, 녹색 및 파란색 구성 요소는 평균화됨) 결과는 대상 배열에 배치됩니다. 이미지는 큰 배열이므로 이 프로세스는 시간이 오래 걸릴 수 있습니다. fork/join 프레임워크를 사용하여 알고리즘을 구현하면 다중 프로세서 시스템에서 동시 처리를 활용할 수 있습니다. 가능한 구현은 다음과 같습니다.
public class ForkBlur extends RecursiveAction {
private int[] mSource;
private int mStart;
private int mLength;
private int[] mDestination;
// Processing window size; should be odd.
private int mBlurWidth = 15;
public ForkBlur(int[] src, int start, int length, int[] dst) {
mSource = src;
mStart = start;
mLength = length;
mDestination = dst;
}
protected void computeDirectly() {
int sidePixels = (mBlurWidth - 1) / 2;
for (int index = mStart; index < mStart + mLength; index++) {
// Calculate average.
float rt = 0, gt = 0, bt = 0;
for (int mi = -sidePixels; mi <= sidePixels; mi++) {
int mindex = Math.min(Math.max(mi + index, 0),
mSource.length - 1);
int pixel = mSource[mindex];
rt += (float)((pixel & 0x00ff0000) >> 16)
/ mBlurWidth;
gt += (float)((pixel & 0x0000ff00) >> 8)
/ mBlurWidth;
bt += (float)((pixel & 0x000000ff) >> 0)
/ mBlurWidth;
}
// Reassemble destination pixel.
int dpixel = (0xff000000 ) |
(((int)rt) << 16) |
(((int)gt) << 8) |
(((int)bt) << 0);
mDestination[index] = dpixel;
}
}
...
Now you implement the abstract compute() method, which either performs the blur directly or splits it into two smaller tasks. A simple array length threshold helps determine whether the work is performed or split.
이제 blur을 직접 수행하거나 두 개의 더 작은 작업으로 분할하는 추상 compute() 메서드를 구현합니다. 단순 배열 길이 임계값은 작업이 수행되는지 또는 분할되는지를 결정하는 데 도움이 됩니다.
protected static int sThreshold = 100000;
protected void compute() {
if (mLength < sThreshold) {
computeDirectly();
return;
}
int split = mLength / 2;
invokeAll(new ForkBlur(mSource, mStart, split, mDestination),
new ForkBlur(mSource, mStart + split, mLength - split,
mDestination));
}
If the previous methods are in a subclass of the RecursiveAction class, then setting up the task to run in a ForkJoinPool is straightforward, and involves the following steps:
이전 메서드가 RecursiveAction 클래스의 하위 클래스에 있는 경우 ForkJoinPool에서 실행할 작업을 설정하는 것은 간단하며 다음 단계를 포함합니다.
- Create a task that represents all of the work to be done.
-
// source image pixels are in src // destination image pixels are in dst ForkBlur fb = new ForkBlur(src, 0, src.length, dst);
- Create the ForkJoinPool that will run the task.
-
ForkJoinPool pool = new ForkJoinPool();
- Run the task.
-
pool.invoke(fb);
For the full source code, including some extra code that creates the destination image file, see the ForkBlur example.
대상 이미지 파일을 생성하는 일부 추가 코드를 포함한 전체 소스 코드는 ForkBlur 예제를 참조하십시오.
Standard Implementations
Besides using the fork/join framework to implement custom algorithms for tasks to be performed concurrently on a multiprocessor system (such as the ForkBlur.java example in the previous section), there are some generally useful features in Java SE which are already implemented using the fork/join framework. One such implementation, introduced in Java SE 8, is used by the java.util.Arrays class for its parallelSort() methods. These methods are similar to sort(), but leverage concurrency via the fork/join framework. Parallel sorting of large arrays is faster than sequential sorting when run on multiprocessor systems. However, how exactly the fork/join framework is leveraged by these methods is outside the scope of the Java Tutorials. For this information, see the Java API documentation.
fork/join 프레임워크를 사용하여 멀티프로세서 시스템에서 동시에 수행할 작업에 대한 사용자 지정 알고리즘을 구현하는 것 외에도(예: 이전 섹션의 ForkBlur.java 예제) Java SE에는 이미 fork/join 프레임워크를 사용하여 구현된 일반적으로 유용한 기능이 있습니다. fork/join 프레임워크. Java SE 8에 도입된 이러한 구현 중 하나는 java.util.Arrays 클래스에서 해당 parallelSort() 메서드에 사용됩니다. 이러한 메서드는 sort()와 유사하지만 fork/join 프레임워크를 통해 동시성을 활용합니다. 대형 배열의 병렬 정렬은 다중 프로세서 시스템에서 실행할 때 순차 정렬보다 빠릅니다. 그러나 fork/join 프레임워크가 이러한 방법에 의해 정확히 어떻게 활용되는지는 Java 자습서의 범위를 벗어납니다. 이 정보는 Java API 문서를 참조하십시오.
Another implementation of the fork/join framework is used by methods in the java.util.streams package, which is part of Project Lambda scheduled for the Java SE 8 release. For more information, see the Lambda Expressions section.
fork/join 프레임워크의 또 다른 구현은 Java SE 8 릴리스로 예정된 Project Lambda의 일부인 java.util.streams 패키지의 메서드에서 사용됩니다. 자세한 내용은 Lambda Expressions 섹션을 참조하십시오.
Concurrent Collections
The java.util.concurrent package includes a number of additions to the Java Collections Framework. These are most easily categorized by the collection interfaces provided:
- BlockingQueue defines a first-in-first-out data structure that blocks or times out when you attempt to add to a full queue, or retrieve from an empty queue.
- ConcurrentMap is a subinterface of java.util.Map that defines useful atomic operations. These operations remove or replace a key-value pair only if the key is present, or add a key-value pair only if the key is absent. Making these operations atomic helps avoid synchronization. The standard general-purpose implementation of ConcurrentMap is ConcurrentHashMap, which is a concurrent analog of HashMap.
- ConcurrentNavigableMap is a subinterface of ConcurrentMap that supports approximate matches. The standard general-purpose implementation of ConcurrentNavigableMap is ConcurrentSkipListMap, which is a concurrent analog of TreeMap.
All of these collections help avoid Memory Consistency Errors by defining a happens-before relationship between an operation that adds an object to the collection with subsequent operations that access or remove that object.
이러한 모든 컬렉션은 객체를 컬렉션에 추가하는 작업과 해당 객체에 액세스하거나 제거하는 후속 작업 간의 사전 발생 관계를 정의하여 메모리 일관성 오류를 방지하는 데 도움이 됩니다.
Atomic Variables
The java.util.concurrent.atomic package defines classes that support atomic operations on single variables. All classes have get and set methods that work like reads and writes on volatile variables. That is, a set has a happens-before relationship with any subsequent get on the same variable. The atomic compareAndSet method also has these memory consistency features, as do the simple atomic arithmetic methods that apply to integer atomic variables.
java.util.concurrent.atomic 패키지는 단일 변수에 대한 원자적 연산을 지원하는 클래스를 정의합니다. 모든 클래스에는 volatile 변수에 대한 읽기 및 쓰기처럼 작동하는 get 및 set 메서드가 있습니다. 즉, set는 동일한 변수에 대한 후속 get과 사전에 발생 관계를 가집니다. 원자 compareAndSet 메서드에는 정수 원자 변수에 적용되는 단순 원자 산술 메서드와 마찬가지로 이러한 메모리 일관성 기능도 있습니다.
To see how this package might be used, let's return to the Counter class we originally used to demonstrate thread interference:
이 패키지가 어떻게 사용될 수 있는지 알아보기 위해 원래 스레드 간섭을 설명하는 데 사용했던 Counter 클래스로 돌아가 보겠습니다.
class Counter {
private int c = 0;
public void increment() {
c++;
}
public void decrement() {
c--;
}
public int value() {
return c;
}
}
One way to make Counter safe from thread interference is to make its methods synchronized, as in SynchronizedCounter:
Counter를 스레드 간섭으로부터 안전하게 만드는 한 가지 방법은 SynchronizedCounter에서와 같이 메서드를 동기화하는 것입니다.
class SynchronizedCounter {
private int c = 0;
public synchronized void increment() {
c++;
}
public synchronized void decrement() {
c--;
}
public synchronized int value() {
return c;
}
}
For this simple class, synchronization is an acceptable solution. But for a more complicated class, we might want to avoid the liveness impact of unnecessary synchronization. Replacing the int field with an AtomicInteger allows us to prevent thread interference without resorting to synchronization, as in AtomicCounter:
이 간단한 클래스의 경우 동기화가 수용 가능한 솔루션입니다. 그러나 더 복잡한 클래스의 경우 불필요한 동기화의 활성 영향(liveness)을 피하고 싶을 수 있습니다. AtomicInteger로 int 필드를 교체하면 AtomicCounter에서와 같이 동기화에 의존하지 않고 스레드 간섭을 방지할 수 있습니다.
import java.util.concurrent.atomic.AtomicInteger;
class AtomicCounter {
private AtomicInteger c = new AtomicInteger(0);
public void increment() {
c.incrementAndGet();
}
public void decrement() {
c.decrementAndGet();
}
public int value() {
return c.get();
}
}
Concurrent Random Numbers
In JDK 7, java.util.concurrent includes a convenience class, ThreadLocalRandom, for applications that expect to use random numbers from multiple threads or ForkJoinTasks.
JDK 7에서 java.util.concurrent에는 다중 스레드 또는 ForkJoinTasks에서 난수를 사용할 것으로 예상되는 애플리케이션을 위한 편의(convenience) 클래스인 ThreadLocalRandom이 포함되어 있습니다.
For concurrent access, using ThreadLocalRandom instead of Math.random() results in less contention and, ultimately, better performance.
동시(concurrent) 액세스의 경우 Math.random() 대신 ThreadLocalRandom을 사용하면 경합이 줄어들고 궁극적으로 성능이 향상됩니다.
All you need to do is call ThreadLocalRandom.current(), then call one of its methods to retrieve a random number. Here is one example:
ThreadLocalRandom.current()를 호출한 다음 메서드 중 하나를 호출하여 난수를 검색하기만 하면 됩니다. 다음은 한 가지 예입니다.
int r = ThreadLocalRandom.current() .nextInt(4, 77);
For Further Reading
- Concurrent Programming in Java: Design Principles and Pattern (2nd Edition) by Doug Lea. A comprehensive work by a leading expert, who's also the architect of the Java platform's concurrency framework.
- Java Concurrency in Practice by Brian Goetz, Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes, and Doug Lea. A practical guide designed to be accessible to the novice.
- Effective Java Programming Language Guide (2nd Edition) by Joshua Bloch. Though this is a general programming guide, its chapter on threads contains essential "best practices" for concurrent programming.
- Concurrency: State Models & Java Programs (2nd Edition), by Jeff Magee and Jeff Kramer. An introduction to concurrent programming through a combination of modeling and practical examples.
- Java Concurrent Animated: Animations that show usage of concurrency features.
'High Level Programming Language > Essential Java Classes' 카테고리의 다른 글
Lesson: Exceptions (0) | 2024.07.14 |
---|---|
Concurrency 2 (0) | 2023.06.04 |
Concurrency 1 (0) | 2023.06.03 |