String、StringBuilder、StringBuffer的区别
String、StringBuilder、StringBuffer
String是常量,看看String类内部的变量就知道
private final byte[] value;//常量的byte,也就是在初始化时就已经固定了,不能再改
String a = "123";
a = a + "45";
执行上面这段代码时,实际上是在常量池中创建了“12345”,然后把变量a指向它。
如果在一个循环中频繁拼接,效率就会很低
在单线程下的执行效率
package string;
/**
* @author: jane
* @CreateTime: 2020/5/8
* @Description: 测试单线程下,各自执行效率
*/
public class SingleThread {
public static void main(String[] args) {
StringCost();
StringBuilderCost();
StringBufferCost();
}
//each time,copy the string to other place.
public static void StringCost(){
long startTime = System.currentTimeMillis();
String a = "";
for(int i=0;i<100000;i++){
a = a + i%10;
}
long endTime = System.currentTimeMillis();
System.out.println("String cost:"+(endTime-startTime));
}
//
public static void StringBuilderCost(){
long startTime = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for(int i=0;i<100000;i++){
sb.append(i%10);
}
long endTime = System.currentTimeMillis();
System.out.println("StringBuilder cost:"+(endTime-startTime));
}
//
public static void StringBufferCost(){
long startTime = System.currentTimeMillis();
StringBuffer sb = new StringBuffer();
for(int i=0;i<100000;i++){
sb.append(i%10);
}
long endTime = System.currentTimeMillis();
System.out.println("StringBuffer cost:"+(endTime-startTime));
}
}
输出
String cost:837
StringBuilder cost:0
StringBuffer cost:10
这样子看,貌似StringBuilder是最优的选择,确实,在单线程情况下,如果需要频繁拼接,使用StringBuilder更好
那在多线程下会有问题吗?
package string;
import java.util.concurrent.CountDownLatch;
/**
* @author: jane
* @CreateTime: 2020/5/8
* @Description: 测试100个线程下的问题
*/
public class MultipleThread {
public static void main(String[] args) {
new Thread(MultipleThread::StringBuilder).start();
new Thread(MultipleThread::StringBuffer).start();
}
//
public static void StringBuilder(){
StringBuilder sb = new StringBuilder();
CountDownLatch builderLatch = new CountDownLatch(100);//类似于信号量,初始值为100
for(int i=0;i<100;i++){
new Thread(() -> {
for(int i1 = 0; i1 <1000; i1++){
sb.append(i1 %10);
}
builderLatch.countDown();//这个线程完成了工作,将信号量减1
}).start();
}
try {
builderLatch.await();//如果值不为0,则会阻塞
String s = sb.toString();
System.out.println("StringBuilder length: "+s.length());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//
public static void StringBuffer(){
StringBuffer sb = new StringBuffer();
CountDownLatch bufferLatch = new CountDownLatch(100);
for(int i=0;i<100;i++){
new Thread(() -> {
for(int i1 = 0; i1 <1000; i1++){
sb.append(i1 %10);
}
bufferLatch.countDown();
}).start();
}
try {
bufferLatch.await();
String s = sb.toString();
System.out.println("StringBuffer length: "+s.length());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
上面还用到了CountDownLatch,主要是用来阻塞主线程,保证100个线程都执行完毕再输出长度;
输出
StringBuilder length: 9156
StringBuffer length: 10000
StringBuilder出现了线程不安全的情况
很清楚,StringBuffer是StringBuilder在多线程下的替换,尽管速度会慢些,但能保证线程安全
看看两个的源代码


就是加了一个synchronized的同步关键字,然后调用父类的append方法,父类的append如下

这个父类是没有同步的;
在StringBuilder中,由于没有用synchronized,可能出现两个线程同时进入到这个方法;
最后导致覆盖的情况(线程一读取到count之后,切换到线程二,也读取count。此时两个线程获取到相同的count,从同一位置写入数据)
有时候可能还会出现ArrayIndexOutOfBoundsException异常,不过我测试了好几次,都没发生。。。
这种情况和解析可以参考这篇博客

浙公网安备 33010602011771号