很多人在回答新手提问的时候会说,StringBuilder 是非同步的,所以快一些,StringBuffer是同步(线程安全)的,所以慢一些。
从技术上说这句话是没有错的,但是为什么多线程的时候仍然用 StringBuilder,不建议用StringBuffer(StringBuffer 建议永远不要使用)?
简单的说,StringBuffer的“线程安全”在大多数时候增加了大量不必要的成本,并且未必达到目的。
看下面的代码:
StringBuffer buffer = getStringBuffer();
for(int i=0; i<10000; i++) buffer.append(i); // append 方法上由关键字 synchronized ,是同步方法
StringBuffer 的每一步操作都先获取同步锁,然后释放同步锁。
所以这段代码中,它获取了10000次锁,释放了10000次锁。
除了获取、释放10000次锁的高昂成本之外,它真的实现了“线程安全”的目的吗?
假如在这个for循环运行的过程中,有另一个线程对 buffer 做了操作:
buffer.append("some string");
那么这个 "some string" 就会插入某两个数字之间!因为在任意两次 append 数字之间,有一个“释放锁”再“获取锁”的极短暂的间隔,这时候是可以被其他线程获取锁的。
换句话说,StringBuffer 所提供的所谓“线程安全”,并不包括多个操作之间的“原子性”支持。
要想前述的for循环不受干扰地完成,用户还是需要手动上锁:
final StringBuffer buffer = getStringBuffer();
synchronized(buffer) {
for(int i=0; i<10000; i++)
buffer.append(i);
}
这样是保证了在for循环运行期间不受其他线程干扰,可是如前所述,10000次锁值 +1 和 -1 的成本还是有的,并且这个成本变成了“完全没有必要”的无意义成本。
这就是为什么 StringBuffer
这个类天生缺陷。
在API设计中有一条规则是这样说的:
一个类不应该自己实现同步,而应该把同步的工作留给用户。
在绝大多数情况下,把同步的工作留给应用代码,而不是工具类的代码。
这是因为: 一个类的用户总是比它的设计者更清楚,什么时候应该同步,应该作什么样的同步,并且,在不同的环境下,用户可能对“如何同步”有不同的需求.
因为同样的原因,Vector 也有这样的天生设计缺陷,java的设计者认识到这种缺陷,所以后来才有了 ArrayList 才有了 StringBuilder。
不幸的是,在API的编写中有这样一种规则:任何公开的东西都已经被冻结,任何签名、接口、承诺、结构等等都不能再改动,因为公开就表示已经有大量的用户代码依赖于此。用户能看到的所有的东西他们都会去依赖,假如你做改动,就会有成千上万的用户代码不能再运行或不能再编译。
所以这两个类还在哪里,但是不表示我们在新的代码里还要去用它们。
最后总结:
100%的情况下不要用 StringBuffer
99% 的情况下不要用 Vector
那么那剩下的 1% 用 Vector 的情况在哪呢?
熟悉Swing的都知道: 现有的一些 model 的类里面用了 Vector,假如你去定制它们,有时不可避免要用到 Vector。
网友评论