This article is participating in “Java Theme Month – Java Debug Notes Event”, see < Event link > for more details.

preface

  • Read the book. I got kicked in the code review for using too muchStringTo perform string operations. We also analyzed the problem and then switched toStringBuilder. Thought everything was fine and shut down for work. Unexpectedly the next day the examination still failed

Problem description

  • Although the switch toStringBuilderUnnecessary GC and memory space usage are avoided. butStringBuilderMethod only applies to a single thread. Concurrency problems occur in multithreaded operations
public static void main(String[] args) throws InterruptedException {
    StringBuilder res = new StringBuilder("");
    List<Thread> threadList = new ArrayList<>();
    CountDownLatch latch = new CountDownLatch(1000);
    for (int i = 0; i < 1000; i++) {
        int finalI = i;
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run(a) {
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                res.append(","+ finalI); latch.countDown(); }}); threadList.add(thread); }for (Thread thread : threadList) {
        thread.start();
    }
    latch.await();
    System.out.println("@ @ @ @ @ @"+res.toString().split(",").length);
}
Copy the code
  • Not surprisingly, the final length is not 1001. It’s probably not 1001. Because concurrency is not 100% guaranteed to go wrong. If it’s not 1001. Readers can try a few more times

Problem orientation

  • The problem is clearly that multiple concurrences cause append errors. Because append basically takes the original character and appends it. So these are two steps and it’s not atomicity but it’s the fact that after you get it the original character is modified by another thread and that thread writes all the new characters from the old data into memory and that causes the data written by the other thread to be lost.
  • We can fix this ourselves by adding a lock before calling the append methodLockorsynchronized

Review again

  • Not surprisingly, it didn’t pass this time. The manager replied that locking was too cumbersome. Not very nice in retrospect. Adding a lock to append not only adds complexity to the code but also makes it easy to forget to release the lock.

  • At this time open Baidu began to learn lessons. StringBuffer is recommended on the web because it is thread-safe.

  • It doesn’t take much work to change the StringBuilder completely to StirngBuffer.

  • But if you look at the source code, StringBuffer#append doesn’t do anything special, except add a lock and an extra parameter to synchronized

@Override
public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}
Copy the code
  • I consoled myself that my manager wouldn’t let me use itsynchronizedThe reason is because I am afraid of bad operation. However,StringBufferToStringCache is used to cache the last character in toString to improve usability.

conclusion

  • Experience is the accumulation of time. If it seemed to me I’d just add a lock and be done with it. But because of not having experienced the baptism of concurrent operation may not be good to lock things
  • Try to use other people’s built-in Java offerings. Don’t build a wheel but know how to build a wheel

You’re welcome to like it