`

对Java的StringBuffer类线程安全的误解

阅读更多

在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)删除了。

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics