До тех пор, пока вы выполняете все операции в одном вызове метода на одновременной карте хэша, вам не нужно использовать дополнительную блокировку. К сожалению, если вам нужно выполнить несколько методов атомарно, вам нужно использовать блокировку, и в этом случае использование параллельной хэш-карты не поможет, и вы также можете использовать простой HashMap.
@ Предложение Джеймс заставило меня подумать о том, ускоряет ли настройка un-need concurrency ConcurrentHashMap быстрее. Это должно уменьшить память, но вам нужно будет иметь тысячи из них, чтобы иметь большое значение. Поэтому я написал этот тест, и не кажется очевидным, что вам всегда нужно настраивать уровень параллелизма.
warmup: Average access time 36 ns.
warmup2: Average access time 28 ns.
1 concurrency: Average access time 25 ns.
2 concurrency: Average access time 25 ns.
4 concurrency: Average access time 25 ns.
8 concurrency: Average access time 25 ns.
16 concurrency: Average access time 24 ns.
32 concurrency: Average access time 25 ns.
64 concurrency: Average access time 26 ns.
128 concurrency: Average access time 26 ns.
256 concurrency: Average access time 26 ns.
512 concurrency: Average access time 27 ns.
1024 concurrency: Average access time 28 ns.
Код
public static void main(String[] args) {
test("warmup", new ConcurrentHashMap());
test("warmup2", new ConcurrentHashMap());
for(int i=1;i<=1024;i+=i)
test(i+" concurrency", new ConcurrentHashMap(16, 0.75f, i));
}
private static void test(String description, ConcurrentHashMap map) {
Integer[] ints = new Integer[2000];
for(int i=0;i<ints.length;i++)
ints[i] = i;
long start = System.nanoTime();
for(int i=0;i<20*1000*1000;i+=ints.length) {
for (Integer j : ints) {
map.put(j,1);
map.get(j);
}
}
long time = System.nanoTime() - start;
System.out.println(description+": Average access time "+(time/20/1000/1000/2)+" ns.");
}
Как @bestss указывает, больший уровень параллелизма может быть медленнее, так как она имеет худшие характеристики кэширования.
EDIT: Кроме того, @betsss беспокоится о том, оптимизируются ли циклы, если нет вызовов методов. Здесь три петли, все равно, но повторяются разные числа раз. Они печатают
10M: Time per loop 661 ps.
100K: Time per loop 26490 ps.
1M: Time per loop 19718 ps.
10M: Time per loop 4 ps.
100K: Time per loop 17 ps.
1M: Time per loop 0 ps.
.
{
int loops = 10*1000 * 1000;
long product = 1;
long start = System.nanoTime();
for(int i=0;i< loops;i++)
product *= i;
long time = System.nanoTime() - start;
System.out.println("10M: Time per loop "+1000*time/loops+" ps.");
}
{
int loops = 100 * 1000;
long product = 1;
long start = System.nanoTime();
for(int i=0;i< loops;i++)
product *= i;
long time = System.nanoTime() - start;
System.out.println("100K: Time per loop "+1000*time/loops+" ps.");
}
{
int loops = 1000 * 1000;
long product = 1;
long start = System.nanoTime();
for(int i=0;i< loops;i++)
product *= i;
long time = System.nanoTime() - start;
System.out.println("1M: Time per loop "+1000*time/loops+" ps.");
}
// code for three loops repeated
не совсем верно. например, для обновления карты может потребоваться несколько вызовов, для которых требуется внешняя синхронизация, но чтение карты все равно может быть одним вызовом метода, и в этом случае ConcurrentMap будет по-прежнему иметь смысл (только с использованием метода обновления, синхронизированного извне). – jtahlborn
Вы говорите, что нужно, чтобы несколько puts выполнялись как одна операция? В этом случае «ConcurrentHashMap» не является допустимым решением, поскольку его уровень атомарности - это одна операция «put». – jdmichal
@jdmichal, да, это моя точка зрения. –