在Java中,有三个操作字符串的类:
* String: 操作不可修改的字符串
* StringBuffer: 操作可修改的字符串,线程安全
* StringBuilder: 操作可修改的字符串,从Java 1.5开始引入,线程不安全,但操作比StringBuffer快。
在开发ugame p7的时候发现我对StringBuffer的线程安全发生误解。
Java的StringBuffer的线程安全并不是说只要操作StringBuffer就是线程安全,
而是说对StringBuffer对象的插入和追加操作对于同一StringBuffer实例而言是原子的,
不允许对同一StringBuffer实例的其它插入和追加操作和这个操作交错执行。
也就是说,多个读出写入的原子操作组成的操作并不原子:
用户界面读取一个StringBuffer静态对象GlobalCall.outputBuffer
然后把它清空:
//用定时器优化日志输出
display.timerExec(TIMER_INTERVAL, new Runnable() {
@Override
public void run() {
//虽然StringBuffer线程安全,
//但在读取toString()和setLength(0)清空之间
//存在交错执行的append输入
//所以要锁住防止交错
synchronized(GlobalCall.outputBuffer) {
if(GlobalCall.outputBuffer.length() > 0) {
logTab.logOutput.append(
GlobalCall.outputBuffer.toString());
GlobalCall.outputBuffer.setLength(0);
}
}
display.timerExec(TIMER_INTERVAL, this);
}
});
另一方面,在后台线程对这个StringBuffer对象执行追加操作
synchronized(outputBuffer) {
outputBuffer.append(getTimeString() + str + "\n");
}
如果不加同步锁就会有问题,原因是,在用户界面线程里,append和setLength(0)执行期间,
后台线程可能对这个线程执行append操作。
结果,在此之间执行的append字符串没有来得及显示到界面上,就被setLength(0)删除了。