【线上】StringBuilder / StringBuffer 线程不安全代码问题

线上有同事写的,特此记录下,也提醒自己不能这么垃圾。


直接给出线上写的垃圾代码, 类似如下

public class Main {

    static StringBuilder sb = new StringBuilder();

    static void test() {
        try {
            // consume from upstream
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (Exception e) {
            e.printStackTrace();
        }
        sb.append("AAA");
        sb.append("\n");
        // append to file
    }


    public static void main(String[] args) throws Exception {
        int threadCnt = 20;
        Thread[] threads = new Thread[threadCnt];
        for(int i=0;i<threadCnt;i++) {
            threads[i] = new Thread(() -> {
                test();
            });
        }
        for(int i=0;i<threadCnt;i++) {
            threads[i].start();
        }
        for(int i=0;i<threadCnt;i++) {
            threads[i].join();
        }
        System.out.println(sb.toString());
    }
}

代码目的:要多线程从上游消费数据,然后写入文件,每一条消费记录写入文件中的一行中。

如上代码会写入如下数据, 不符合要求

【线上】StringBuilder / StringBuffer 线程不安全代码问题_第1张图片

原因:首先:test方法不是线程安全的,其次:StringBuilder是非线程安全的

那么换成StringBuffer, 多跑几次仍然有问题

public class Main {

//    static StringBuilder sb = new StringBuilder();
    static StringBuffer sb = new StringBuffer();

    static void test() {
        try {
            // consume from upstream
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (Exception e) {
            e.printStackTrace();
        }
        sb.append("AAA");
        sb.append("\n");
        // append to file
    }


    public static void main(String[] args) throws Exception {
        int threadCnt = 20;
        Thread[] threads = new Thread[threadCnt];
        for(int i=0;i<threadCnt;i++) {
            threads[i] = new Thread(() -> {
                test();
            });
        }
        for(int i=0;i<threadCnt;i++) {
            threads[i].start();
        }
        for(int i=0;i<threadCnt;i++) {
            threads[i].join();
        }
        System.out.println(sb.toString());
    }
}

原因:test方法不是线程安全的,为什么写代码要如下两句,弄的线程不安全

sb.append("AAA");
sb.append("\n");

所以最后是

public class Main {

    static StringBuffer sb = new StringBuffer();

    static void test() {
        try {
            // consume from upstream
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (Exception e) {
            e.printStackTrace();
        }
        sb.append("AAA\n");
        // append to file
    }


    public static void main(String[] args) throws Exception {
        int threadCnt = 20;
        Thread[] threads = new Thread[threadCnt];
        for(int i=0;i<threadCnt;i++) {
            threads[i] = new Thread(() -> {
                test();
            });
        }
        for(int i=0;i<threadCnt;i++) {
            threads[i].start();
        }
        for(int i=0;i<threadCnt;i++) {
            threads[i].join();
        }
        System.out.println(sb.toString());
    }
}

这样无论怎么运行,结果都是正确的,如下符合

AAA
AAA
AAA
AAA
AAA
AAA
AAA
AAA
AAA
AAA
AAA
AAA
AAA
AAA
AAA
AAA
AAA
AAA
AAA
AAA

总结:StringBuffer & StringBuilder 要分清,不要写很莫名的垃圾代码

你可能感兴趣的:(Java)