如何在高线程负载场景下移除代码中的synchronized同步机制?
高并发场景下Java代码的优化方案
原代码核心问题
原代码使用**方法级synchronized**导致所有请求串行执行,完全丧失并发能力;同时ArrayDeque并非线程安全容器,仅靠方法同步规避风险,在高并发下性能瓶颈明显。你想基于postId实现细粒度锁的思路是对的,下面针对String.intern()的可行性及整体优化给出具体建议:
关于String.intern()的使用风险
不推荐用String.intern()作为锁对象,原因如下:
- 内存泄漏风险:
intern()后的字符串会存入元空间(原永久代),若postId数量极大且持续新增,会导致元空间溢出OOM; - 性能开销:
intern()操作需访问字符串常量池,高并发下会产生额外竞争; - 不可控性:无法主动释放
interned字符串,长期运行可能积累无效内存占用。
更稳妥的方案是用ConcurrentHashMap维护锁对象池,每个postId对应唯一锁实例,既能实现细粒度隔离,又能灵活控制内存。
具体优化步骤
1. 替换线程不安全容器
将ArrayDeque换成线程安全的ConcurrentLinkedDeque,避免细粒度锁下的容器并发修改问题。
2. 实现细粒度锁机制
用ConcurrentHashMap存储锁对象,通过computeIfAbsent保证同一postId复用同一锁:
private final ConcurrentMap<String, Post> postById = new ConcurrentHashMap<>(); private final Deque<Post> posts = new ConcurrentLinkedDeque<>(); private final ConcurrentHashMap<String, Object> lockMap = new ConcurrentHashMap<>(); private final int maxPostsPerUser; private final AtomicInteger postCount = new AtomicInteger(0); // 用原子类维护队列大小,替代O(n)的size() // 获取对应postId的锁对象 private Object getLock(String postId) { return lockMap.computeIfAbsent(postId, k -> new Object()); }
3. 优化addPost方法
public Post addPost(String postId, Date createdAt, String link, String profileId) { Object lock = getLock(postId); synchronized (lock) { Post newPost = new Post(postId, link, createdAt, profileId); Post existingPost = postById.putIfAbsent(postId, newPost); if (existingPost != null) { // 替换旧帖子并从队列中移除 postById.replace(postId, newPost); posts.remove(existingPost); postCount.decrementAndGet(); } // 循环判断队列大小,避免多线程并发添加导致超量 while (postCount.get() >= maxPostsPerUser) { Post removedPost = posts.removeLast(); postById.remove(removedPost.getId()); postCount.decrementAndGet(); } posts.addFirst(newPost); postCount.incrementAndGet(); return newPost; } }
4. 优化addUpdate方法
public Post addUpdate(String postId, Date createdAt, long numReplies, long numReposts, long numLikes, String link, String profileId) { Object lock = getLock(postId); synchronized (lock) { Post existingPost = postById.get(postId); if (existingPost != null) { // 直接更新属性,用volatile/原子类保证可见性 existingPost.setNumReplies(numReplies); existingPost.setNumReposts(numReposts); existingPost.setNumLikes(numLikes); return existingPost; } else { // 帖子不存在,执行新增逻辑 Post newPost = new Post(postId, link, createdAt, profileId); newPost.setNumReplies(numReplies); postById.put(postId, newPost); while (postCount.get() >= maxPostsPerUser) { Post removedPost = posts.removeLast(); postById.remove(removedPost.getId()); postCount.decrementAndGet(); } posts.addFirst(newPost); postCount.incrementAndGet(); return newPost; } } }
5. 保证Post类的线程安全
为Post的可变属性添加volatile修饰(或用原子类),确保锁外读取操作的可见性:
public class Post { private final String id; private final String link; private final Date createdAt; private final String profileId; private volatile long numReplies; private volatile long numReposts; private volatile long numLikes; public Post(String id, String link, Date createdAt, String profileId) { this.id = id; this.link = link; this.createdAt = createdAt; this.profileId = profileId; } // getter/setter方法 public void setNumReplies(long numReplies) { this.numReplies = numReplies; } // 其他setter/getter省略 }
6. 优化队列删除效率
重写Post的equals和hashCode方法,基于postId实现,让ConcurrentLinkedDeque.remove()能快速定位元素:
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Post post = (Post) o; return Objects.equals(id, post.id); } @Override public int hashCode() { return Objects.hash(id); }
内容的提问来源于stack exchange,提问作者Gandalf
相关产品推荐
相关产品推荐

