How does StringBuffer really work in the design of thread-safe programs?
本问题已经有最佳答案,请猛点这里访问。
很多人提到了StringBuffer和StrugBuube在Java中的区别。StringBuffer包含同步方法。人们会说"如果一个缓冲区被许多线程使用,就使用StringBuffer",但是使用StringBuffer真的会保证"线程安全"吗?
好吧,我认为强调一下StringBuffer的一些实际用途很重要。为此,我设计了一个简单的程序来说明在实现线程安全性方面,StringBuffer如何优于StringBuilder。
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | /** * Run this program a couple of times. We see that the StringBuilder does not * give us reliable results because its methods are not thread-safe as compared * to StringBuffer. * * For example, the single append in StringBuffer is thread-safe, i.e. * only one thread can call append() at any time and would finish writing * back to memory one at a time. In contrast, the append() in the StringBuilder * class can be called concurrently by many threads, so the final size of the * StringBuilder is sometimes less than expected. * */ public class StringBufferVSStringBuilder { public static void main(String[] args) throws InterruptedException { int n = 10; //*************************String Builder Test*******************************// StringBuilder sb = new StringBuilder(); StringBuilderTest[] builderThreads = new StringBuilderTest[n]; for (int i = 0; i < n; i++) { builderThreads[i] = new StringBuilderTest(sb); } for (int i = 0; i < n; i++) { builderThreads[i].start(); } for (int i = 0; i < n; i++) { builderThreads[i].join(); } System.out.println("StringBuilderTest: Expected result is 1000; got" + sb.length()); //*************************String Buffer Test*******************************// StringBuffer sb2 = new StringBuffer(); StringBufferTest[] bufferThreads = new StringBufferTest[n]; for (int i = 0; i < n; i++) { bufferThreads[i] = new StringBufferTest(sb2); } for (int i = 0; i < n; i++) { bufferThreads[i].start(); } for (int i = 0; i < n; i++) { bufferThreads[i].join(); } System.out.println("StringBufferTest: Expected result is 1000; got" + sb2.length()); } } // Every run would attempt to append 100"A"s to the StringBuilder. class StringBuilderTest extends Thread { StringBuilder sb; public StringBuilderTest (StringBuilder sb) { this.sb = sb; } @Override public void run() { for (int i = 0; i < 100; i++) { sb.append("A"); } } } //Every run would attempt to append 100"A"s to the StringBuffer. class StringBufferTest extends Thread { StringBuffer sb2; public StringBufferTest (StringBuffer sb2) { this.sb2 = sb2; } @Override public void run() { for (int i = 0; i < 100; i++) { sb2.append("A"); } } } |
然而,正如许多其他人所指出的,对于设计线程安全应用程序来说,StringBuffer并不是一个奇迹。更进一步,我会说,用于并发性的工具和库(例如向量)应该被很好地理解和适当地实现,并且我们不应该对使用"线程安全"库做出简单的假设。
http://jeremymanson.blogspot.sg/2008/08/dont-use-stringbuffer.html
杰里米的例子说明了这一点,我引用:
1 2 3 4 5 6 7 8 9 |
"当然,它是"线程安全的",从某种意义上说,没有数据争用(基本上是没有足够同步的并发访问)。但是你不知道第三条线会打印什么:"ab"或"ba"。我必须引入更多的同步,以使其产生合理的结果。StringBuffer附带的锁没有任何帮助。"
我希望这对你是有见地的!