String,StringBuffer与StringBuilder的区别

String 字符串常量

功能获取方法:
length();获取字符串长度
concact();拼接字符串
CharAt();获取相应 下表的字符
indexOf();搜索相应的字符并返回该字符第一次出现的位置
subString();截取字符串,返回从包含下标--------不包含制定位置的字符串
split();分割字符串
replace();替换
4.2转换功能方法:
toCharArray();把字符串转换成字符赎罪
getBytes();把字符串转换成字节数组

案例

/**
 * 定义一个StringBuffer类对象,
 * 1)使用append方法向对象中添加26个字母,并倒序遍历输入
 * 2)删除前五个字符
 * @author TCBpersonalcomputer
 *
 */
public class Test {
    public static void main(String[] args){
        StringBuffer buf=new StringBuffer();
        //循环添加26个小写字母
        for(int i=97;i<123;i++){
            buf.append((char)i);
        }
        //倒序遍历输出:方法一,使用for循环令i=buf.length()-1,i--输出
        //方式二,先将buf逆序,在遍历输出。如下
        buf.reverse();
        for(int i=0;i<buf.length();i++){
            System.out.print(buf.charAt(i)+" ");
        }
        //删除前五个字符的方法
        //first
        System.out.println();
        System.out.println("2.删除前5个字符");
        buf.reverse();
        buf.delete(0, 5);
        for(int i=0;i<buf.length();i++){
            System.out.print(buf.charAt(i)+" ");
        }
        //方法二:使用循环buf.deleteCharAt(i)【删除指定下标的字符】
    }
}

运行结果: 

StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(非线程安全)

简要的说, String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的。
而如果是使用 StringBuffer 类则结果就不一样了,每次结果都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象引用。所以在一般情况下我们推荐使用 StringBuffer ,特别是字符串对象经常改变的情况下。而在某些特别情况下, String 对象的字符串拼接其实是被 JVM 解释成了 StringBuffer 对象的拼接,所以这些时候 String 对象的速度并不会比 StringBuffer 对象慢,而特别是以下的字符串对象生成中, String 效率是远要比 StringBuffer 快的:
String S1 = “This is only a” + “ simple” + “ test”;
StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);
你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个
String S1 = “This is only a” + “ simple” + “test”; 其实就是:
String S1 = “This is only a simple test”; 所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如:
String S2 = “This is only a”;
String S3 = “ simple”;
String S4 = “ test”;
String S1 = S2 +S3 + S4;
这时候 JVM 会规规矩矩的按照原来的方式去做


在大部分情况下 StringBuffer > String
StringBuffer
Java.lang.StringBuffer线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。
可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。
StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。
例如,如果 z 引用一个当前内容是“start”的字符串缓冲区对象,则此方法调用 z.append("le") 会使字符串缓冲区包含“startle”,而 z.insert(4, "le") 将更改字符串缓冲区,使之包含“starlet”。
在大部分情况下 StringBuilder > StringBuffer
java.lang.StringBuilde
java.lang.StringBuilder一个可变的字符序列是5.0新增的。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。两者的方法基本相同
 

String是Java中基础且重要的类,并且String也是Immutable类的典型实现,被声明为final class,除了hash这个属性其它属性都声明为final,因为它的不可变性,所以例如拼接字符串时候会产生很多无用的中间对象,如果频繁的进行这样的操作对性能有所影响。

StringBuffer就是为了解决大量拼接字符串时产生很多中间对象问题而提供的一个类,提供append和add方法,可以将字符串添加到已有序列的末尾或指定位置,它的本质是一个线程安全的可修改的字符序列,把所有修改数据的方法都加上了synchronized。但是保证了线程安全是需要性能的代价的。

在很多情况下我们的字符串拼接操作不需要线程安全,这时候StringBuilder登场了,StringBuilder是JDK1.5发布的,它和StringBuffer本质上没什么区别,就是去掉了保证线程安全的那部分,减少了开销。

StringBuffer 和 StringBuilder 二者都继承了 AbstractStringBuilder ,底层都是利用可修改的char数组(JDK 9 以后是 byte数组)。

所以如果我们有大量的字符串拼接,如果能预知大小的话最好在new StringBuffer 或者StringBuilder 的时候设置好capacity,避免多次扩容的开销。扩容要抛弃原有数组,还要进行数组拷贝创建新的数组。

我们平日开发通常情况下少量的字符串拼接其实没太必要担心,例如

String str = "aa"+"bb"+"cc";

像这种没有变量的字符串,编译阶段就直接合成"aabbcc"了,然后看字符串常量池(下面会说到常量池)里有没有,有也直接引用,没有就在常量池中生成,返回引用。

如果是带变量的,其实影响也不大,JVM会帮我们优化了。(以下运行JDK版本为1.8)

再说说字符串常量池

看看我们的代码,你会发现String是真的频繁得使用到,Java为了避免在一个系统中产生大量的String对象,引入了字符串常量池。

创建一个字符串时,首先会检查池中是否有值相同的字符串对象,如果有就直接返回引用,不会创建字符串对象;如果没有则新建字符串对象,返回对象引用,并且将新创建的对象放入池中。但是,通过new方法创建的String对象是不检查字符串常量池的,而是直接在堆中创建新对象,也不会把对象放入池中。上述原则只适用于直接给String对象引用赋值的情况。

String str1 = new String("a"); //不检查字符串常量池的

String str2 = "bb"; //检查字符串常量池的

String还提供了intern()方法。调用该方法时,如果字符串常量池中包括了一个等于此String对象的字符串(由equals方法确定),则返回池中的字符串的引用。否则,将此String对象添加到池中,并且返回此池中对象的引用。

在JDK6中,不推荐大量使用intern方法,因为这个版本字符串缓存在永久代中,这个空间是有限了,除了FullGC之外不会被清楚,所以大量的缓存在这容易OutOfMemoryError。

之后的版本把字符串放入了堆中,避免了永久代被挤满。

总结

1、在字符串不经常发生变化的业务场景优先使用String(代码更清晰简洁)。如常量的声明,少量的字符串操作(拼接,删除等)。

2、在单线程情况下,如有大量的字符串操作情况,应该使用StringBuilder来操作字符串。不能使用String"+"来拼接而是使用,避免产生大量无用的中间对象,耗费空间且执行效率低下(新建对象、回收对象花费大量时间)。如JSON的封装等。

3、在多线程情况下,如有大量的字符串操作情况,应该使用StringBuffer。如HTTP参数解析和封装等。

StringBuffer在单线程和多线程环境中的区别

这道题重点在synchronized的四种锁的状态,分别为:无锁状态,偏向锁状态,轻量级锁,重量级锁

因为StringBuffer是synchronized保证线程安全的,单线程下synchronized默认加的是偏向锁,多线程则是加重量级锁

锁膨胀(又称锁升级):
在大多数情况下,锁总是由同一线程多次获得,不存在多线程竞争,所以出现了偏向锁。其目标就是在只有一个线程执行同步代码块时能够提高性能。

当一个线程访问同步代码块并获取锁时,会在Mark Work(对象头)中存储锁偏向的线程ID.在线程进入和退出的时候不需要CAS加锁和解锁,而是检查MarkWord里是否存储着指向当前线程的偏向锁

线程不会主动释放偏向锁,需要等待全局安全点(在这个时间点上没有字节码正在执行),它会首先暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态。撤销偏向锁后恢复到无锁(锁标志位为“01”)或轻量级锁(锁标志位为“00”)的状态。

当有其他线程访问同步代码块,偏向锁就会升级为轻量级锁,其他线程通过自旋形式尝试获取锁,不会阻塞,从而提高性能

若当前只有一个等待线程,则该线程通过自旋进行等待。但是当自旋超过一定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁升级为重量级锁。

升级为重量级锁时,锁标志的状态值变为“10”,此时Mark Word中存储的是指向重量级锁的指针,此时等待锁的线程都会进入阻塞状态。

总结:
偏向锁通过对比Mark Word解决加锁问题,避免执行CAS操作。而轻量级锁是通过用CAS操作和自旋来解决加锁问题,避免线程阻塞和唤醒而影响性能。重量级锁是将除了拥有锁的线程以外的线程都阻塞

分布式,高并发,多线程之间有啥区别

这三个词估计是现如今博客区或招聘网站上最常看到的字样了,我想大部分不接触大型互联网企业的程序员都很难接触这些东西。心向往之,但无奈没机会接触。平时多线程遇到到的还算多。分布式和高并发在企业信息管理系统中可能不多见。当面试官问起这三个词的时候,是不试试很多人都认为分布式=高并发=多线程? 一开始接触的时候可能很多都会混淆。所以总结一下:

1. 什么是分布式?

 分布式是一个概念,是为了解决单个物理服务器容量和性能瓶颈问题而采用的优化手段。该领域需要解决的问题很多,在不同的技术层面上,又包含:分布式文件系统,分布式数据库,分布式缓存,分布式计算等等,一些名词,如:Hadoop, zookeeper,MQ等都跟分布式有关。从理念上讲,分布式的实现有两种方式:水平扩展, 当一台机器的容量抗不住的时候,需要增加机器的方式,将流量平均到所有服务器上,所有机器都可以提供相当的服务;.垂直扩展,前端有多种需求查询时,一台机器扛不住,可以将不同的需求分发到不同的机器上,比如A机器处理余票查询的时候,B机器可以处理支付请求等。

2. 什么是高并发

相对于分布式来讲,高并发在解决的问题上会集中一些,其反应的是同时有多少流量进来。比如:在线直播,秒杀等,同时有上万人观看,参抢。高并发可以用分布式来解决,将并发的流量分到不同的物理服务器上。但除此外,还可以有很多其他的优化手段,比如:使用缓存,将所有的,静态内容放到CDN等。 还可以使用多线程技术将一台服务的服务能力最大化。

3. 什么是多线程

多线程是指从软件或硬件上实现多个线程并发执行的技术。它更多的是解决CPU调度多个进程的问题,从而让这些进程看上去是同时执行。这几个概念中,多线程解决的问题是很明确的,手动也是比较单一的,基本上遇到的最大问题就是线程安全。这JAVA语言中,需要对JVM内存模型,指令重排序等深入了解,才能写出一份高质量的多线程代码。

总结一下:

分布式是从物理资源的角度去将不同的机器组成一个整体对外提供服务的,技术范围非常广且难度非常大,有了这个基础,高并发,高吞吐等系统很容易实现。

高并发是从以业务角度去描述系统的能力,实现高并发的手段可以采用分布式,也可以采用诸如:缓存,CND等,也包括多线程。

多线程则聚集于如何使用编程语言将CPU调度能力最大化。

原文地址:https://www.cnblogs.com/h-c-g/p/11081298.html