String、StringBuilder和StringBuffer的一些理解

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());
        }
    }
   
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值