昨天我发表了一篇名为《从字节码角度看String的连接操作》的博文,我提到String的"+"操作是非线程安全的,有网友给我留言能否提供一个证实String的"+"操作是非线程安全的的例子,所以今天用例子来证实这个结论。
假设有这样的示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
public class TestString { public final static int THREAD_SIZE = 50; /** * 同步各个线程的运行,因为每个线程只执行一行代码, * 所以等所有的线程产生后一起执行,这样才能最大限度地并发 */ public transient int i = 0; public String s = ""; //public StringBuffer sb = new StringBuffer(); public static void main(String[] args) throws Exception { final TestString map = new TestString(); Runnable runnable = new Runnable() { @Override public void run() { while(map.i != 1){ try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } map.s +="|"; //map.sb.append("|"); } }; for(int i =0; i<THREAD_SIZE; i++) { Thread t = new Thread(runnable); t.start(); } map.i = 1; Thread.sleep(2000); //map.s = map.sb.toString(); if(THREAD_SIZE != map.s.length()) { System.err.println("length:" + map.s.length()); } else { System.err.println("correct"); } } } |
示例很简单,启动了THREAD_SIZE个线程,每个线程调用map.s +="|"向该字符串中追加一个'|',如果是线程安全的,那么等所以线程结束后,那么s的长度应该等于THREAD_SIZE,当我们通过反复的运行该示例发现,大多数时候字符串s的长度都不等于线程的数目,从而说明该操作是非线程安全的。
如果大家知道String的"+"操作的原理,相信对这个结论一点都不惊讶,如果不熟悉可以看看我的上篇博文,很简单也不会耽误太多的时间,可能你要问,要是我想要字符串的连接操作是线程安全的,那么该怎么改呢,其一可以自己使用synchronized进行代码块的同步,其二是将字符串的连接操作使用StringBuffer对象进行,其实我示例中注释掉的代码就是使用StringBuffer的版本,无论你运行多少次,那么输出的都是"correct"
Posted in: java基础
Comments are closed.