JUC
线程安全的集合类-ConcurrentHashMap(1)
错误用法:
1.将26个英文字母打散,每个字母出现两百次,将字母平均分到26个txt文件中。
先创建测试文件
/**
* 初始化数据
* 单词计数
* @author jiangl
* @version 1.0
* @date 2021/4/15 14:58
*/
public class InitDataClass{
static final String ALPHA = "abcdefghijklmnopqrstuvwxyz";
public static void main(String[] args) {
int length = ALPHA.length();
//字母出现次数是200次
int count = 200;
List<String> list = new ArrayList<>(length * count);
for(int i = 0;i< length ;i++){
char ch = ALPHA.charAt(i);
for(int j=0;j<count;j++){
//将字母加入到集合
list.add(String.valueOf(ch));
}
}
//打散
Collections.shuffle(list);
for(int i=0;i<26;i++){
//存入26个文件
try(PrintWriter out = new PrintWriter(
new OutputStreamWriter(
new FileOutputStream("tmp/"+(i+1)+".txt")))){
String collect = list.subList(i * count,(i + 1) * count).stream()
.collect(Collectors.joining("\n"));
out.print(collect);
}catch (IOException e){
e.printStackTrace();
}
}
}
}
2.遍历文件,分别统计26个字母出现个数
使用HashMap
/**
* @author jiangl
* @version 1.0
* @date 2021/4/15 14:57
*/
public class ConcurrentHashMapTest {
public static void main(String[] args) {
fail1();
}
/**
* 使用HashMap作为容器返回结果,失败,原因是HashMap本身线程不安全,并且,map.get() 和 map.put()两个操作不是原子操作
*/
private static void fail1() {
demo(
//创建map集合
//创建concurrentHashMap 对不对?
//使用HashMap失败
()->new HashMap<String,Integer>(),
(map,words)->{
for(String word : words){
synchronized (map){
Integer counter = map.get(word);
int newValue = counter == null ? 1 : counter + 1;
map.put(word,newValue);
}
}
}
);
}
private static <V> void demo(Supplier<Map<String,V>> supplier, BiConsumer<Map<String,V>, List<String>> consumer){
Map<String,V> counterMap = supplier.get();
List<Thread> ts = new ArrayList<>();
for(int i=1;i<=26;i++){
int idx = i;
Thread thread = new Thread(()->{
List<String> words = readFromFile(idx);
consumer.accept(counterMap,words);
});
ts.add(thread);
}
ts.forEach(t->t.start());
ts.forEach(t->{
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(counterMap);
}
/**
*
* @param i
* @return
*/
private static List<String> readFromFile(int i){
ArrayList<String> words = new ArrayList<>();
try(BufferedReader in = new BufferedReader(
new InputStreamReader(new FileInputStream("tmp/"+i+".txt")))) {
while(true){
String word = in.readLine();
if(word == null){
break;
}
words.add(word);
}
return words;
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
输出结果:
{a=197, b=196, c=199, d=200, e=200, f=200, g=199, h=200, i=196, j=197, k=198, l=200, m=196, n=198, o=199, p=194, q=197, r=197, s=197, t=199, u=198, v=198, w=200, x=200, y=200, z=199}
有结果可以看出,使用HashMap线程不安全,并且,map.get() 和 map.put()两个操作不是原子操作,所以导致统计错误。
解决方法:
加锁synchorinized
使用concurrentHashMap
/**
* @author jiangl
* @version 1.0
* @date 2021/4/15 14:57
*/
public class ConcurrentHashMapTest {
public static void main(String[] args) {
fail2();
}
private static void fail2() {
demo(
//创建map集合
//创建concurrentHashMap 对不对?
//使用ConcurrentHashMap 也是失败的,由于map.get() 和 map.put()两个操作分开执行了不是原子操作
() -> new ConcurrentHashMap<String, Integer>(),
(map, words) -> {
for (String word : words) {
Integer counter = map.get(word);
int newValue = counter == null ? 1 : counter + 1;
map.put(word, newValue);
}
}
);
}
private static <V> void demo(Supplier<Map<String,V>> supplier, BiConsumer<Map<String,V>, List<String>> consumer){
Map<String,V> counterMap = supplier.get();
List<Thread> ts = new ArrayList<>();
for(int i=1;i<=26;i++){
int idx = i;
Thread thread = new Thread(()->{
List<String> words = readFromFile(idx);
consumer.accept(counterMap,words);
});
ts.add(thread);
}
ts.forEach(t->t.start());
ts.forEach(t->{
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(counterMap);
}
/**
*
* @param i
* @return
*/
private static List<String> readFromFile(int i){
ArrayList<String> words = new ArrayList<>();
try(BufferedReader in = new BufferedReader(
new InputStreamReader(new FileInputStream("tmp/"+i+".txt")))) {
while(true){
String word = in.readLine();
if(word == null){
break;
}
words.add(word);
}
return words;
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
测试结果:
{a=193, b=194, c=193, d=197, e=197, f=198, g=198, h=195, i=195, j=193, k=195, l=194, m=193, n=197, o=196, p=197, q=198, r=193, s=197, t=192, u=193, v=195, w=198, x=192, y=194, z=195}
使用ConcurrentHashMap依旧无法正常统计,原因是ConcurrentHashMap的put(),get()方法本身是线程安全的,但是两个操作一起操作的时候却不是原子操作,例如代码段:
Integer counter = map.get(word);
int newValue = counter == null ? 1 : counter + 1;
map.put(word, newValue);
所以还是无法获取正确结果。
解决办法1.加锁
2.解决办法使用concurrentHashMap + LongAdder 调用map.computeIfAbsent 成功
/**
* @author jiangl
* @version 1.0
* @date 2021/4/15 14:57
*/
public class ConcurrentHashMapTest {
public static void main(String[] args) {
success2();
}
private static void success2() {
demo(
//使用concurrentHashMap + LongAdder 调用map.computeIfAbsent 成功
()->new ConcurrentHashMap<String, LongAdder>(),
(map,words)->{
for(String word : words){
//如果缺少一个key,则计算生成一个value,然后将 key value放入map;
LongAdder integer = map.computeIfAbsent(word, (key) -> new LongAdder());
//执行累加
integer.increment();
}
}
);
}
private static <V> void demo(Supplier<Map<String,V>> supplier, BiConsumer<Map<String,V>, List<String>> consumer){
Map<String,V> counterMap = supplier.get();
List<Thread> ts = new ArrayList<>();
for(int i=1;i<=26;i++){
int idx = i;
Thread thread = new Thread(()->{
List<String> words = readFromFile(idx);
consumer.accept(counterMap,words);
});
ts.add(thread);
}
ts.forEach(t->t.start());
ts.forEach(t->{
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(counterMap);
}
/**
*
* @param i
* @return
*/
private static List<String> readFromFile(int i){
ArrayList<String> words = new ArrayList<>();
try(BufferedReader in = new BufferedReader(
new InputStreamReader(new FileInputStream("tmp/"+i+".txt")))) {
while(true){
String word = in.readLine();
if(word == null){
break;
}
words.add(word);
}
return words;
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
结果:
{a=200, b=200, c=200, d=200, e=200, f=200, g=200, h=200, i=200, j=200, k=200, l=200, m=200, n=200, o=200, p=200, q=200, r=200, s=200, t=200, u=200, v=200, w=200, x=200, y=200, z=200}
由此获得到了正确的结果。