在多线程环境下获取每个字符串被获取的次数需要使用线程安全的数据结构来存储和更新计数。在Java中,你可以使用ConcurrentHashMap
来实现这个功能。
下面是一个示例代码,展示如何使用多线程统计字符串被获取的次数:
package com.example.demo.service;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Slf4j
public class Threadmore {
private ConcurrentHashMap<String, Integer> countMap = new ConcurrentHashMap<>();
public void increment(String str) {
countMap.putIfAbsent(str, 0);
countMap.computeIfPresent(str, (key, value) -> value + 1);
}
public int getCount(String str) {
return countMap.getOrDefault(str, 0);
}
public static void main(String[] args) {
Threadmore threadmore = new Threadmore();
ExecutorService service = Executors.newFixedThreadPool(5);
//创建任务
Runnable task = () -> {
String str = "abc";
threadmore.increment(str);
};
for (int i = 0; i < 10; i++) {
service.submit(task);
}
//关闭线程池
service.shutdown();
//任务为执行完继续的等待
while (!service.isTerminated()) {
//等待。。。。。
}
log.info("字符数:{}", threadmore.getCount("abc"));
}
}
在上面的代码中,ThreadMore
类使用ConcurrentHashMap
作为计数器的存储结构。increment
方法用于增加指定字符串的计数,它使用了putIfAbsent
方法来确保在第一次获取字符串时初始化计数为0,然后使用computeIfPresent
方法对已存在的字符串计数进行自增操作。
getCount
方法用于获取指定字符串的计数,它使用getOrDefault
方法来获取字符串的计数,如果该字符串还没有被获取过,则返回0。
注意,ConcurrentHashMap
提供了线程安全的操作,可以在多个线程之间安全地进行并发访问和更新。这样,多个线程可以同时调用increment
方法来增加计数,而不会产生竞争条件或数据不一致的问题。
ConcurrentHashMap
是Java标准库提供的线程安全的哈希表实现。在多线程环境下,多个线程可以同时对ConcurrentHashMap
进行读取和写入操作,而不会导致数据不一致或产生竞争条件。
下面是一些使用ConcurrentHashMap
的原因:
-
线程安全性:
ConcurrentHashMap
提供了内部的锁机制,保证了在并发情况下的线程安全性。多个线程可以同时对ConcurrentHashMap
进行读取和写入操作,而不会导致数据损坏或不一致的情况。 -
高性能:
ConcurrentHashMap
在内部实现上采用了分段锁的方式,将整个哈希表分成多个段,每个段上都有一把锁。这样,在多线程环境下,不同的线程可以同时访问不同的段,减小了锁的竞争,提高了并发性能。 -
可伸缩性:
ConcurrentHashMap
的设计允许多个线程同时对不同的段进行操作,从而提供了更好的可伸缩性。在高并发情况下,ConcurrentHashMap
可以有效地支持大量的读取和写入操作,而不会成为性能瓶颈。 -
功能完善:
ConcurrentHashMap
提供了丰富的操作方法,如putIfAbsent
、computeIfPresent
等,使得在多线程环境下进行复杂的操作变得更加方便和安全。
ConcurrentHashMap
的底层原理是基于分段锁(Segment)和哈希表(Hash table)实现的。
-
分段锁(Segment):
ConcurrentHashMap
将整个哈希表分成多个段(Segment),每个段都维护了一个子哈希表(Hash table)。每个段都拥有自己的锁,多个线程可以同时访问不同的段,减小了锁的竞争范围,提高了并发性能。 -
哈希表(Hash table):每个段内部使用哈希表来存储键值对。哈希表的底层数据结构是一个数组,每个数组元素称为一个桶(Bucket)。每个桶可以存储一个或多个键值对,通过哈希算法将键映射到对应的桶中。
-
线程安全操作:
ConcurrentHashMap
使用分段锁来实现线程安全的操作。当多个线程对不同的段进行操作时,它们可以同时进行,不会相互阻塞。当多个线程对同一个段进行操作时,只有该段上的锁被获取,其他段仍然可以被访问。这样,在多线程环境下,ConcurrentHashMap
可以提供高效的并发性能。 -
扩容:当
ConcurrentHashMap
的负载因子(load factor)达到阈值时,会触发扩容操作。扩容会创建一个更大的哈希表,并将原有的键值对重新分配到新的桶中。在扩容过程中,仍然可以对ConcurrentHashMap
进行读取和写入操作,不会阻塞其他线程。
总结起来,ConcurrentHashMap
通过分段锁和哈希表的组合实现了高效的线程安全操作。每个段可以独立进行操作,多个线程可以同时对不同的段进行读取和写入操作,提高了并发性能。同时,它还提供了高度可伸缩性和功能完善的操作方法,使其成为处理并发访问的理想选择。