User.java
package org.example.chm3;
/**
* @author jianan
* @date 2021/7/6 15:44:29
*/
public class User {
private String username;
private int age;
public User(String username, int age) {
this.username = username;
this.age = age;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
UserService.java
package org.example.chm3;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
/**
* @author jianan
* @date 2021/7/6 15:44:10
*/
public class UserService {
private static Logger logger = LoggerFactory.getLogger(UserService.class);
private static Map userMap = Maps.newConcurrentMap();
// 不安全的写法
public static boolean registerUnsafe(User user) {
if (userMap.containsKey(user.getUsername())) {
logger.info("用户已存在");
return false;
} else {
userMap.put(user.getUsername(), user);
logger.info("用户注册成功, {},{}", user.getUsername(), user.getAge());
return true;
}
}
// 安全的写法
public static boolean registerSafe(User user) {
User user1 = userMap.putIfAbsent(user.getUsername(), user);
if (user1 != null){
logger.info("用户已存在");
return false;
} else{
logger.info("用户注册成功, {},{}", user.getUsername(), user.getAge());
return true;
}
}
}
不安全的写法测试
package org.example.chm3;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
/**
* @author jianan
* @date 2021/7/6 15:42:53
*/
public class TestCHM3 {
public static void main(String[] args) throws InterruptedException {
int threadCount = 8;
ForkJoinPool forkJoinPool = new ForkJoinPool(threadCount);
forkJoinPool.execute(() -> {
IntStream.range(0, threadCount).mapToObj(i -> new User("张三", i))
.parallel()
.forEach(UserService::registerUnsafe);
});
TimeUnit.SECONDS.sleep(1);
}
}
/*
[2021-07-06 15:54:21,117] [ForkJoinPool-1-worker-15] INFO org.example.chm3.UserService.register(UserService.java:20): 用户已存在
[2021-07-06 15:54:21,119] [ForkJoinPool-1-worker-1] INFO org.example.chm3.UserService.register(UserService.java:24): 用户注册成功, 张三,0
[2021-07-06 15:54:21,119] [ForkJoinPool-1-worker-13] INFO org.example.chm3.UserService.register(UserService.java:24): 用户注册成功, 张三,6
[2021-07-06 15:54:21,119] [ForkJoinPool-1-worker-5] INFO org.example.chm3.UserService.register(UserService.java:24): 用户注册成功, 张三,2
[2021-07-06 15:54:21,119] [ForkJoinPool-1-worker-3] INFO org.example.chm3.UserService.register(UserService.java:24): 用户注册成功, 张三,5
[2021-07-06 15:54:21,117] [ForkJoinPool-1-worker-9] INFO org.example.chm3.UserService.register(UserService.java:20): 用户已存在
[2021-07-06 15:54:21,117] [ForkJoinPool-1-worker-7] INFO org.example.chm3.UserService.register(UserService.java:20): 用户已存在
[2021-07-06 15:54:21,117] [ForkJoinPool-1-worker-11] INFO org.example.chm3.UserService.register(UserService.java:20): 用户已存在
*/
安全的写法测试
package org.example.chm3;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
/**
* @author jianan
* @date 2021/7/6 15:56:34
*/
public class TestCHM4 {
public static void main(String[] args) throws InterruptedException {
int threadCount = 8;
ForkJoinPool forkJoinPool = new ForkJoinPool(threadCount);
forkJoinPool.execute(() -> {
IntStream.range(0, threadCount).mapToObj(i -> new User("张三", i))
.parallel()
.forEach(UserService::registerSafe);
});
TimeUnit.SECONDS.sleep(1);
}
}
/*
[2021-07-06 15:57:52,678] [ForkJoinPool-1-worker-9] INFO org.example.chm3.UserService.register2(UserService.java:32): 用户已存在
[2021-07-06 15:57:52,678] [ForkJoinPool-1-worker-1] INFO org.example.chm3.UserService.register2(UserService.java:32): 用户已存在
[2021-07-06 15:57:52,678] [ForkJoinPool-1-worker-11] INFO org.example.chm3.UserService.register2(UserService.java:32): 用户已存在
[2021-07-06 15:57:52,680] [ForkJoinPool-1-worker-5] INFO org.example.chm3.UserService.register2(UserService.java:32): 用户已存在
[2021-07-06 15:57:52,680] [ForkJoinPool-1-worker-7] INFO org.example.chm3.UserService.register2(UserService.java:32): 用户已存在
[2021-07-06 15:57:52,680] [ForkJoinPool-1-worker-15] INFO org.example.chm3.UserService.register2(UserService.java:32): 用户已存在
[2021-07-06 15:57:52,679] [ForkJoinPool-1-worker-13] INFO org.example.chm3.UserService.register2(UserService.java:32): 用户已存在
[2021-07-06 15:57:52,678] [ForkJoinPool-1-worker-3] INFO org.example.chm3.UserService.register2(UserService.java:36): 用户注册成功, 张三,5
*/
笔记:
这里我们使用了Map提供的putIfAbsent接口,其含义是如果 key 已经存在则返回存储的对象,否则返回null。
putIfAbsent接口定义的时候不是线程安全的,但ConcurrentHashMap在实现的时候将这个方法实现为线程安全。
在这个场景中如果不使用putIfAbsent就要对register(User user)方法加锁,对于性能的影响更大。