ConcurrentHashMap 값을 반복하여 스레드 세이프입니까?
javadoc for ConcurrentHashMap은 다음과 같습니다.
일반적으로 검색 작업(get 포함)은 차단되지 않으므로 업데이트 작업(put 및 remove 포함)과 중복될 수 있습니다.검색은 시작 시 가장 최근에 완료된 업데이트 작업의 결과를 반영합니다.putAll 및 clear와 같은 집약 작업의 경우 동시 검색에는 일부 항목의 삽입 또는 제거만 반영될 수 있습니다.마찬가지로 반복자 및 열거자는 반복자/계수 작성 시점 또는 이후 어느 시점에서 해시 테이블의 상태를 반영하는 요소를 반환합니다.Concurrent Modification은 슬로우하지 않습니다.예외.그러나 반복기는 한 번에 하나의 스레드에서만 사용하도록 설계되었습니다.
그것은 무엇을 뜻하나요?두 개의 스레드를 동시에 사용하여 지도를 반복하려고 하면 어떻게 됩니까?값을 반복할 때 맵에서 값을 넣거나 삭제하면 어떻게 됩니까?
그것은 무엇을 뜻하나요?
, 개, 개개에서 한 각 ConcurrentHashMap
는 단일 스레드에서 사용하도록 설계되어 있으므로 전달하지 마십시오.이것은 각 루프가 제공하는 통사당을 포함한다.
두 개의 스레드를 동시에 사용하여 지도를 반복하려고 하면 어떻게 됩니까?
각 스레드가 자체 반복기를 사용하는 경우 예상대로 작동합니다.
값을 반복할 때 맵에서 값을 넣거나 삭제하면 어떻게 됩니까?
. ('가 '동시'가 ).ConcurrentHashMap
수단)을 참조해 주세요.단, (지도에서 새 반복기를 가져오지 않고) 한 스레드가 다른 스레드가 실행하는 맵의 변경을 인식할 수 있다는 보장은 없습니다.반복기는 맵 작성 시점의 상태를 반영합니다.더 큰 변화는 반복기에 반영될 수 있지만 반드시 반영될 필요는 없습니다.
결론적으로 다음과 같은 진술이 있습니다.
for (Object o : someConcurrentHashMap.entrySet()) {
// ...
}
거의 매번 괜찮아요(또는 적어도 안전해요).
는 공유 변환 할 수 .ConcurrentHashMap
:
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ConcurrentMapIteration
{
private final Map<String, String> map = new ConcurrentHashMap<String, String>();
private final static int MAP_SIZE = 100000;
public static void main(String[] args)
{
new ConcurrentMapIteration().run();
}
public ConcurrentMapIteration()
{
for (int i = 0; i < MAP_SIZE; i++)
{
map.put("key" + i, UUID.randomUUID().toString());
}
}
private final ExecutorService executor = Executors.newCachedThreadPool();
private final class Accessor implements Runnable
{
private final Map<String, String> map;
public Accessor(Map<String, String> map)
{
this.map = map;
}
@Override
public void run()
{
for (Map.Entry<String, String> entry : this.map.entrySet())
{
System.out.println(
Thread.currentThread().getName() + " - [" + entry.getKey() + ", " + entry.getValue() + ']'
);
}
}
}
private final class Mutator implements Runnable
{
private final Map<String, String> map;
private final Random random = new Random();
public Mutator(Map<String, String> map)
{
this.map = map;
}
@Override
public void run()
{
for (int i = 0; i < 100; i++)
{
this.map.remove("key" + random.nextInt(MAP_SIZE));
this.map.put("key" + random.nextInt(MAP_SIZE), UUID.randomUUID().toString());
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
private void run()
{
Accessor a1 = new Accessor(this.map);
Accessor a2 = new Accessor(this.map);
Mutator m = new Mutator(this.map);
executor.execute(a1);
executor.execute(m);
executor.execute(a2);
}
}
예외는 발생하지 않습니다.
액세스 스레드 간에 동일한 반복기를 공유하면 교착 상태가 발생할 수 있습니다.
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ConcurrentMapIteration
{
private final Map<String, String> map = new ConcurrentHashMap<String, String>();
private final Iterator<Map.Entry<String, String>> iterator;
private final static int MAP_SIZE = 100000;
public static void main(String[] args)
{
new ConcurrentMapIteration().run();
}
public ConcurrentMapIteration()
{
for (int i = 0; i < MAP_SIZE; i++)
{
map.put("key" + i, UUID.randomUUID().toString());
}
this.iterator = this.map.entrySet().iterator();
}
private final ExecutorService executor = Executors.newCachedThreadPool();
private final class Accessor implements Runnable
{
private final Iterator<Map.Entry<String, String>> iterator;
public Accessor(Iterator<Map.Entry<String, String>> iterator)
{
this.iterator = iterator;
}
@Override
public void run()
{
while(iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
try
{
String st = Thread.currentThread().getName() + " - [" + entry.getKey() + ", " + entry.getValue() + ']';
} catch (Exception e)
{
e.printStackTrace();
}
}
}
}
private final class Mutator implements Runnable
{
private final Map<String, String> map;
private final Random random = new Random();
public Mutator(Map<String, String> map)
{
this.map = map;
}
@Override
public void run()
{
for (int i = 0; i < 100; i++)
{
this.map.remove("key" + random.nextInt(MAP_SIZE));
this.map.put("key" + random.nextInt(MAP_SIZE), UUID.randomUUID().toString());
}
}
}
private void run()
{
Accessor a1 = new Accessor(this.iterator);
Accessor a2 = new Accessor(this.iterator);
Mutator m = new Mutator(this.map);
executor.execute(a1);
executor.execute(m);
executor.execute(a2);
}
}
Iterator<Map.Entry<String, String>>
및 스레드 중java.lang.IllegalStateException
가 뜨기 시작합니다.
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ConcurrentMapIteration
{
private final Map<String, String> map = new ConcurrentHashMap<String, String>();
private final Iterator<Map.Entry<String, String>> iterator;
private final static int MAP_SIZE = 100000;
public static void main(String[] args)
{
new ConcurrentMapIteration().run();
}
public ConcurrentMapIteration()
{
for (int i = 0; i < MAP_SIZE; i++)
{
map.put("key" + i, UUID.randomUUID().toString());
}
this.iterator = this.map.entrySet().iterator();
}
private final ExecutorService executor = Executors.newCachedThreadPool();
private final class Accessor implements Runnable
{
private final Iterator<Map.Entry<String, String>> iterator;
public Accessor(Iterator<Map.Entry<String, String>> iterator)
{
this.iterator = iterator;
}
@Override
public void run()
{
while (iterator.hasNext())
{
Map.Entry<String, String> entry = iterator.next();
try
{
String st =
Thread.currentThread().getName() + " - [" + entry.getKey() + ", " + entry.getValue() + ']';
} catch (Exception e)
{
e.printStackTrace();
}
}
}
}
private final class Mutator implements Runnable
{
private final Random random = new Random();
private final Iterator<Map.Entry<String, String>> iterator;
private final Map<String, String> map;
public Mutator(Map<String, String> map, Iterator<Map.Entry<String, String>> iterator)
{
this.map = map;
this.iterator = iterator;
}
@Override
public void run()
{
while (iterator.hasNext())
{
try
{
iterator.remove();
this.map.put("key" + random.nextInt(MAP_SIZE), UUID.randomUUID().toString());
} catch (Exception ex)
{
ex.printStackTrace();
}
}
}
}
private void run()
{
Accessor a1 = new Accessor(this.iterator);
Accessor a2 = new Accessor(this.iterator);
Mutator m = new Mutator(map, this.iterator);
executor.execute(a1);
executor.execute(m);
executor.execute(a2);
}
}
즉, 여러 스레드 간에 반복기 개체를 공유하면 안 됩니다.여러 개의 반복기를 생성하여 별도의 스레드에서 동시에 사용하는 것이 좋습니다.
이것은 당신에게 좋은 통찰력을 줄 것이다.
ConcurrentHashMap은 발신자에 대한 약속을 약간 완화함으로써 높은 동시성을 실현합니다.검색 조작은 가장 최근에 완료된 삽입 조작에 의해 삽입된 값을 반환하고 동시에 진행 중인 삽입 조작에 의해 추가된 값을 반환할 수도 있습니다(그러나 어떤 경우에도 넌센스 결과를 반환하지 않습니다).ConcurrentHashMap.iterator()에 의해 반환된 반복자는 최대 1회 각 요소를 반환하며 ConcurrentModification을 슬로우하지 않습니다.예외. 그러나 반복기 구성 이후 발생한 삽입 또는 제거를 반영하거나 반영하지 않을 수 있습니다.컬렉션을 반복할 때 스레드 안전성을 제공하기 위해 테이블 전체의 잠금이 필요하지 않습니다(또는 가능하지도 않습니다).ConcurrentHashMap은 업데이트를 방지하기 위해 테이블 전체를 잠그는 기능에 의존하지 않는 응용 프로그램에서 synchronizedMap 또는 해시테이블을 대체할 수 있습니다.
이것에 대해서:
그러나 반복기는 한 번에 하나의 스레드에서만 사용하도록 설계되었습니다.
즉, ConcurrentHashMap에서 생성된 반복기를 2개의 스레드에서 사용하는 것은 안전하지만 응용 프로그램에서 예기치 않은 결과를 초래할 수 있습니다.
그것은 무엇을 뜻하나요?
즉, 두 스레드에 동일한 반복기를 사용하려고 하면 안 됩니다.키, 값 또는 엔트리에 대해 반복해야 하는 두 개의 스레드가 있는 경우 각각 자체 반복기를 만들어 사용해야 합니다.
두 개의 스레드를 동시에 사용하여 지도를 반복하려고 하면 어떻게 됩니까?
당신이 이 규칙을 어기면 무슨 일이 일어날지 완전히 명확하지는 않다.(예를 들어) 2개의 스레드가 동기화하지 않고 표준 입력에서 읽으려고 할 때와 같은 방법으로 혼란스러운 동작이 발생할 수 있습니다.스레드 세이프가 아닌 동작도 할 수 있습니다.
하지만 두 스레드가 서로 다른 반복기를 사용했다면 괜찮을 겁니다.
값을 반복할 때 맵에서 값을 넣거나 삭제하면 어떻게 됩니까?
두 스레드가 동일한 반복기를 사용하는 경우: 위 항목을 참조하십시오.혼란스럽고 스레드 세이프가 아닌 행동을 하게 될 가능성이 있습니다.
스레드가 다른 반복기를 사용하는 경우 인용한 javadoc 섹션이 적절하게 응답합니다.기본적으로 한 스레드/반복기가 다른 스레드/반복기에 의한 동시 삽입, 업데이트 또는 삭제의 영향을 볼지는 정의되어 있지 않습니다.그러나 삽입/갱신/삭제는 맵의 동시성 속성에 따라 이루어집니다.
언급URL : https://stackoverflow.com/questions/3768554/is-iterating-concurrenthashmap-values-thread-safe
'programing' 카테고리의 다른 글
Vue에서 v-model.trim의 목적은 무엇입니까? (0) | 2022.08.28 |
---|---|
Vue.js를 사용하여 목록에서 다른 목록으로 이동하는 작업을 애니메이션화하는 방법 (0) | 2022.08.28 |
주석을 사용하여 구성된 스프링 빈에 속성 값을 삽입하려면 어떻게 해야 합니까? (0) | 2022.08.28 |
프리프로세서 디렉티브를 사용하여 OS를 확인하려면 어떻게 해야 합니까? (0) | 2022.08.28 |
'static' 키워드는 클래스에서 무엇을 합니까? (0) | 2022.08.28 |