java中stringbulider和stringbuffer安全性的解释

关于java中stringbuffer和stringbulider安全性的学习


(2021.10.29写于山威)

1.关于他们同时继承的抽象类AbstractStringBuilder内部

这张图片内部一共有三个变量,分别是value和计数长度count,而我们平时使用的string类,内部的byte []value数组是使用final来修饰的,意味不可变,这也就是我们常说的string类不可变的原因,而且在AbstractStringBuilder类内部也都定义了这两个类的基本操作,append方法,而我们之所以说stringbulider不安全,stringbuffer安全很大一部分上是和这个有关的

2.测试代码

测试stringbulider的不安全性

*
	 * 复习开启线程的三种方式:
	 * 1.类继承thread,override run方法之后start
	 * 2.类继承runable接口,实现run方法,之后使用线程中的方法开启
	 * 3.匿名类new thread(new runable{}).start;
	 * */
	//线程在等待,睡眠或以其他方式占用时抛出,线程在活动之前或活动期间中断。InterruptedException
	public static void main( String [] args) throws InterruptedException
	{
		//默认构造方式创建stringbulider
		StringBuilder str=new StringBuilder();
		//StringBuffer safestr=new StringBuffer();
		for(int i=0;i<10;i++)
		{
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					for(int i=0;i<100;i++)
					{
						str.append('a');
						try {
							//在线程睡眠的时候,就很有可能被其他线程抢占cpu 的执行权
							Thread.sleep(10);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						System.out.println("stringBulider的长度是+ "+str.length()+" 当前线程的名字: "+Thread.currentThread());
					}
					
				}
			}).start();//启动线程
			
			
			
		}
		System.out.println(str.toString());
	}

**在上面代码中,我们就很有可能抛出问题,因为我们在线程中每次给str.append()的时候,都会让线程挂起一段时间,而在这段时间中,当有另外一个线程抢占之后使用共享资源str,就会造成问题,具体为什么,我们后面再说
-->ps:这一段很有可能抛出ArrayIndexoutException异常
我个人这里的输出信息

(没有抛出异常我很尴尬,但是显而易见的是对于同一长度,不同线程连续输出了好几次)

测试stringbuffer

public static void main( String [] args) throws InterruptedException
	{
		//默认构造方式创建stringbulider
		//StringBuilder str=new StringBuilder();
		StringBuffer safestr=new StringBuffer();
		for(int i=0;i<10;i++)
		{
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					for(int i=0;i<100;i++)
					{
						safestr.append('a');
						try {
							//在线程睡眠的时候,就很有可能被其他线程抢占cpu 的执行权
							Thread.sleep(10);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						System.out.println("stringBulider的长度是+ "+safestr.length()+Thread.currentThread());
					}
					
				}
			}).start();//启动线程
			
			
			
		}
		System.out.println(safestr.toString());
	}

这个也只是做了小小的更改,但是这会就不会跑出上面的原因了。

3.探究原因

我们深入进去源码看看,就可以了解到一部分原因:
首先是append方法的定义
stringbuffer的定义


我们可以清楚的看见他使用了 synchronized关键字保证了每次append的时候,当一个线程访问,就会阻塞其他线程的访问,这样一来就保证了同步方法的执行,但是也具有很大的开销
stringbulider的定义

append内部对于count数值的变化
但是上面的分析还是无法解释当第一个代码块有时候会抛出arrayindexoutexception异常的原因,我们可以加入append方法内部查看

内部对于count有一个+len的操作,而因为这个操作如果不能保证同步,比如我thread1拿到的count是10,thread2拿到的count也是10,两者同时对于一个共享的count进行+1,本应该count变为12,而这个时候就只会变为11,从而影响了后续访问,以及capacity的扩容操作。
本人学的不怎么样,只能了解到这么多 --2021.10.29

posted @ 2021-10-29 15:13  写不完作业还要玩  阅读(130)  评论(0编辑  收藏  举报