java - Trying to remove synchronization from thread-heavy workload - Stack Overflow

I came across some code in our code base that seems not wrong, but terribly inefficient due the the nat

I came across some code in our code base that seems not wrong, but terribly inefficient due the the nature of our platform (a lot of threads could be accessing these two methods simultaneously).

ConcurrentMap<String, Post> postById = new ConcurrentHashMap<>();
ArrayDeque<Post> posts = new Dequeue<>();

synchronized public Post addPost(String postId, Date createdAt, String link, String profileId) {
    Post post = new Post(postId, link, createdAt, profileId);
    if (postsById.containsKey(postId)) {
        postsById.replace(postId, post);
        for (Post removeCandidate: posts) {
            if (removeCandidate.getId().equals(postId)) {
                posts.remove(removeCandidate);
                break;
            }
        }
    }
    if (posts.size() >= maxPostsPerUser) {
        Post removed = posts.removeLast();
        postsById.remove(removed.getId());
    }
    postsById.put(postId, post);
    posts.addFirst(post);   
    
    return post;
}

synchronized public Post addUpdate(String postId, Date createdAt, long numReplies, long numReposts, long numLikes, String link, String profileId) {
    Post post = postsById.get(postId);
    if (post != null) {
        post.setNumReplies(numReplies);
        post.setNumReposts(numReposts);
        post.setNumLikes(numLikes);
    } else {
        post = new Post(postId, link, createdAt, profileId);
        post.setNumReplies(numReplies);
        if (posts.size() >= maxPostsPerUser) {
            Post removed = posts.removeLast();
            postsById.remove(removed.getId());
        }
        postsById.put(postId, post);
        posts.addFirst(post);           
    }
    
    return post;
}

I personally hate synchronized blocks anywhere (yes, sometimes it's the only way, rarely)

But in this scenario I am at a slight loss. I was thinking of locking access on the postId somehow, so contention would be at a much finer grain. Not sure if String.intern() would make this possible. Looking for suggestions.

I came across some code in our code base that seems not wrong, but terribly inefficient due the the nature of our platform (a lot of threads could be accessing these two methods simultaneously).

ConcurrentMap<String, Post> postById = new ConcurrentHashMap<>();
ArrayDeque<Post> posts = new Dequeue<>();

synchronized public Post addPost(String postId, Date createdAt, String link, String profileId) {
    Post post = new Post(postId, link, createdAt, profileId);
    if (postsById.containsKey(postId)) {
        postsById.replace(postId, post);
        for (Post removeCandidate: posts) {
            if (removeCandidate.getId().equals(postId)) {
                posts.remove(removeCandidate);
                break;
            }
        }
    }
    if (posts.size() >= maxPostsPerUser) {
        Post removed = posts.removeLast();
        postsById.remove(removed.getId());
    }
    postsById.put(postId, post);
    posts.addFirst(post);   
    
    return post;
}

synchronized public Post addUpdate(String postId, Date createdAt, long numReplies, long numReposts, long numLikes, String link, String profileId) {
    Post post = postsById.get(postId);
    if (post != null) {
        post.setNumReplies(numReplies);
        post.setNumReposts(numReposts);
        post.setNumLikes(numLikes);
    } else {
        post = new Post(postId, link, createdAt, profileId);
        post.setNumReplies(numReplies);
        if (posts.size() >= maxPostsPerUser) {
            Post removed = posts.removeLast();
            postsById.remove(removed.getId());
        }
        postsById.put(postId, post);
        posts.addFirst(post);           
    }
    
    return post;
}

I personally hate synchronized blocks anywhere (yes, sometimes it's the only way, rarely)

But in this scenario I am at a slight loss. I was thinking of locking access on the postId somehow, so contention would be at a much finer grain. Not sure if String.intern() would make this possible. Looking for suggestions.

Share Improve this question edited Mar 22 at 8:59 Mark Rotteveel 110k229 gold badges156 silver badges224 bronze badges asked Mar 21 at 20:06 GandalfGandalf 9,8558 gold badges58 silver badges91 bronze badges 3
  • 1 If this is 'working' code the you should ask in codereview.stackexchange. I imagine that it can have some issues with both methods modifying concurrently the ArrayDeque however you didn't mention any specific issues. synchronized will prevent concurrent access to a single method. It would be also important to explain how these methods are used. – aled Commented Mar 21 at 20:48
  • @aled synchronized will not just "prevent concurrent access to a single method", it will prevent all concurrent access that synchronizes on the same monitor. – Mark Rotteveel Commented Mar 22 at 9:00
  • @MarkRotteveel my mistake, thanks for the clarification. – aled Commented Mar 22 at 17:36
Add a comment  | 

1 Answer 1

Reset to default 0

Came across some code in our code base that seems (not wrong) but terribly inefficient due the the nature of our platform (a lot of threads could be accessing these two methods simultaneously.

Before we get into the details of your problem ... do you have any evidence that this really is inefficient? That it has an actual impact on performance? That the impact is significant?

If not, this could be a case of premature optimization. My advice would be to use a profiler or some other technique to figure out whether there is a real problem here ... before investing lots of time in solving it.


Looking at the code, it strikes me that the for loop in addPost is a potential performance bottleneck. Since it is performed while holding the lock, it is a potential concurrency bottleneck as well. So:

  • See if there is a way to do the removal from posts more efficiently.
  • See if there is a way to do it without holding the lock; e.g. by relaxing the implicit constraints.
  • Maybe see if you can avoid the removal entirely; e.g. if the purpose of the posts Deque is to be a work queue, you could check if a post is the latest for a user before processing it. That is an O(1) operation.

Profiling should tell you if the for loop is actually a performance bottleneck. So profile first!

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744335193a4569079.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信