String、StringBuilder和StringBuffer的一些理解
前段时间去面试了一些公司,算是考核一下两年来的java功底吧,发现其实java的很多深层次的东西理解还不够,原以为看了thinking in java和jvm的一些原理,就对java很了解了,现在看来,还差得很远,正好这段时间有空,抽出空来仔细对jdk的一些类做些研究学习一下。也顺带把自己的java能力再进一步提升。忘了说了,原本一直做java web,根本不太注意这些线程的东东,只知道要用struts,spring,ibatis等一些开源框架,很多事情都交给它们,连至于为什么要在项目中使用StringBuffer来代替String的拼接都不太了解,只知道StringBuffer会比String拼接快。
为了后面更好的解释StringBuilder和StringBuffer的线程安全问题,这里先对线程安全做一些解释,如果对此比较了解的朋友,就可以跳过了。
1.线程安全
线程安全的问题是建立在共享资源的问题上的,我们都知道一个进程中会有一个或多个线程,而多个线程之所以不用多个进程替代,是因为在一个进程中,多线程是可以共享进程中的资源,而恰恰是这些共享的资源,如果你不控制好,在多个线程同时访问并修改共享资源就会出现错误,如果每个线程都同时执行这样一个操作:“都先读出共享资源,再修改共享资源,并将修改后的共享资源放回”,这样就会产生线程不安全的问题,共享资源的数据就存在异常。举个实际的例子,比如有一笔公费1000元(相当于共享资源),有A、B、C、D、E5个人,每个人都需要拿出100元来开销(这5个人相当于5个线程),现在A、B、C、D、E同时拿到公费1000元,各自都拿出100元,然后A告诉共享资源还有900元,B、C、D、E也一样。A、B、C、D、E都拿出了100元,但是系统中公费却还有900元,这很明显数据上出现了错误,出错的原因就在于这5个人并发操作了共享数据。
为了解决这种对共享数据的并发操作而出现的问题,java提供了一个关键字“synchronized”,可以用在方法或代码段。
3.String (不可变)
说完上面的,现在来开始说说String,String从jdk1.0就开始存在了,而且String是不可修改的,意思就是说一旦你创建了一个String,并赋值了,那么这个String的内容将一直保持不变,直到这个String对象销毁。细心的朋友可能会问,String str = "hello"; str = str + "world"; 这个代码String的值不是改变了吗?那我来告诉你,这个其实并不是原先的String了,而是在完成+操作后,新创建了一个String,并将变量str指向新创建的String对象。在Jdk中有这样一段话“The Java language provides special support for the string concatenation operator ( + ), and for conversion of other objects to strings. String concatenation is implemented through the StringBuilder(or StringBuffer) class and its append method”,大意说“java对String提供了特殊的加法操作支持,并且这个String的连接实现是采用StringBuilder和StringBuffer的append方法完成的"。(tip:StringBuilder是jdk1.5之后新增的)
那么String是否存在线程安全的问题呢?这个可以肯定的告诉你String是不存在线程安全的,之所以存在线程安全问题,是因为共享资源的可变性,但是java中的String是一次赋值后就不可改变的,当然就更不可能存在线程安全的问题了。
4.StringBuilder (线程不安全)
引用jdk中的原文“This class provides an API compatible with StringBuffer, but with no guarantee of synchronization",大意是“这个类提供了和StringBuffer相兼容的API,但是没有同步的保证“,这个'没有同步的保证'也就是说在多线程的环境下,会有线程安全问题;这个类建议在单线程环境下使用,他的性能会比StringBuffer好些,如果使用时指定了大小,性能会有很大的提升,有些人测试后,说能提升30%以上,具体我没试过,感兴趣的朋友可以试验一下。
那怎么测试StringBuilder是否是线程不安全的呢?下面给出一段代码,5个线程并发执行,同时对共享数据sb操作,正常的情况下,sb最后长度应该是2000,但是实验中你会发现,多次执行的结果都不一样,这就是因为多个线程同时操作了StringBuilder,而它的append方法没有同步控制,所以会有多个线程同时调用append方法,但是最后sb只加了一个字符,长度只增加1,而不是多个字符(每个线程一次一个字符)。
public class SingleThread implements Runnable {
private static StringBuilder sb = new StringBuilder();
private String name;
public SingleThread(String name) {
this.name = name;
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
SingleThread thread1 = new SingleThread("thread1");
new Thread(thread1).start();
SingleThread thread2 = new SingleThread("thread2");
new Thread(thread2).start();
SingleThread thread3 = new SingleThread("thread3");
new Thread(thread3).start();
SingleThread thread4 = new SingleThread("thread4");
new Thread(thread4).start();
SingleThread thread5 = new SingleThread("thread5");
new Thread(thread5).start();
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(sb.length());
}
public void run() {
for (int i=0; i<400; i++) {
sb.append(";");
System.out.println(name+":"+sb.toString());
}
}
}
5.StringBuffer (线程安全)
StringBuffer其实和StringBuilder基本上是差不多,只不过在StringBuffer中,很多方法都使用了synchronized,来控制程序的安全性。
与StringBuilder相比,StringBuffer就没有sb最后的长度不一致的问题,无论你执行多少次,结果都是一样的,长度都是2000.
public class SingleThread implements Runnable {
private static StringBuffer sb = new StringBuffer();
private String name;
public SingleThread(String name) {
this.name = name;
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
SingleThread thread1 = new SingleThread("thread1");
new Thread(thread1).start();
SingleThread thread2 = new SingleThread("thread2");
new Thread(thread2).start();
SingleThread thread3 = new SingleThread("thread3");
new Thread(thread3).start();
SingleThread thread4 = new SingleThread("thread4");
new Thread(thread4).start();
SingleThread thread5 = new SingleThread("thread5");
new Thread(thread5).start();
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(sb.length());
}
public void run() {
for (int i=0; i<400; i++) {
sb.append(";");
System.out.println(name+":"+sb.toString());
}
}
}
String、StringBuilder和StringBuffer的一些理解
最新推荐文章于 2024-09-14 18:46:05 发布