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