一、String 字符串常量
String 是
不可变对象
,每次对 String 对象进行改变都会生成一个新的 String 对象,然后将指针指向新的 String 对象,故经常改变内容的字符串最好不要用 String 。因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度就更加慢了。
package com.nobody.part01;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
/**
* @author Mr.nobody
* @Description 测试String字符串拼接性能
* @date 2020/9/17
*/
public class StringDemo {
// 请求总数
private static int CLIENT_COUNT = 10000;
// 并发线程数
private static int THREAD_COUNT = 500;
// 全局变量
private static String STRING = new String();
public static void main(String[] args) throws InterruptedException {
// 固定线程池
ExecutorService executorService = Executors.newFixedThreadPool(CLIENT_COUNT);
// 控制同一个时刻,只能有多少个线程同时运行指定代码,即acquire和release之间的代码
Semaphore semaphore = new Semaphore(THREAD_COUNT);
CountDownLatch countDownLatch = new CountDownLatch(CLIENT_COUNT);
long startTime = System.currentTimeMillis();
for (int i = 0; i < CLIENT_COUNT; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
STRING += "0";
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
});
}
// 等待所有请求执行完
countDownLatch.await();
// 关闭线程池
executorService.shutdown();
long endTime = System.currentTimeMillis();
System.out.println("String -- 总共耗时:" + (endTime - startTime) + "ms,字符串长度:" + STRING.length());
}
}
二、StringBuffer 字符串变量
StringBuffer 是
可变对象
,每次对 StringBuffer 对象进行改变都会对StringBuffer 对象本身进行操作。而不是生成新的对象,再改变对象引用。如果 StringBuffer 对象在多线程环境下,特别是字符串对象经常改变的情况下,推荐使用它 。因为 StringBuffer 几乎所有的方法都加了synchronized关键字,所以是线程安全的,但是性能会相对较差。
package com.nobody.part01;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
/**
* @author Mr.nobody
* @Description 测试线程安全类StringBuffer
* @date 2020/9/17
*/
public class StringBufferDemo {
// 请求总数
private static int CLIENT_COUNT = 10000;
// 并发线程数
private static int THREAD_COUNT = 500;
// 全局变量,线程安全StringBuffer
private static StringBuffer STRINGBUFFER = new StringBuffer();
public static void main(String[] args) throws InterruptedException {
// 固定线程池
ExecutorService executorService = Executors.newFixedThreadPool(CLIENT_COUNT);
// 控制同一个时刻,只能有多少个线程同时运行指定代码,即acquire和release之间的代码
Semaphore semaphore = new Semaphore(THREAD_COUNT);
CountDownLatch countDownLatch = new CountDownLatch(CLIENT_COUNT);
long startTime = System.currentTimeMillis();
for (int i = 0; i < CLIENT_COUNT; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
STRINGBUFFER.append("0");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
});
}
// 等待所有请求执行完
countDownLatch.await();
// 关闭线程池
executorService.shutdown();
long endTime = System.currentTimeMillis();
System.out.println("StringBuffer -- 总共耗时:" + (endTime - startTime) + "ms,字符串长度:" + STRINGBUFFER.length());
}
}
三、StringBuilder 字符串变量
StringBuilder 一个
可变的字符序列
。StringBuilder 提供一个与 StringBuffer 兼容的 API,但不保证同步(即
线程不安全
)。该类被设计用作 StringBuffer 的一个简易替换,
用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)
。在堆栈封闭等线程安全的环境下(方法中的局部变量)应该首选 StringBuilder。
package com.nobody.part01;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
/**
* @author Mr.nobody
* @Description 测试线程不安全类StringBuilder
* @date 2020/9/17
*/
public class StringBuilderDemo {
// 请求总数
private static int CLIENT_COUNT = 10000;
// 并发线程数
private static int THREAD_COUNT = 500;
// 全局变量,线程不安全StringBuilder
private static StringBuilder STRINGBUILDER = new StringBuilder();
public static void main(String[] args) throws InterruptedException {
// 固定线程池
ExecutorService executorService = Executors.newFixedThreadPool(CLIENT_COUNT);
// 控制同一个时刻,只能有多少个线程同时运行指定代码,即acquire和release之间的代码
Semaphore semaphore = new Semaphore(THREAD_COUNT);
CountDownLatch countDownLatch = new CountDownLatch(CLIENT_COUNT);
long startTime = System.currentTimeMillis();
for (int i = 0; i < CLIENT_COUNT; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
STRINGBUILDER.append("0");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
});
}
// 等待所有请求执行完
countDownLatch.await();
// 关闭线程池
executorService.shutdown();
long endTime = System.currentTimeMillis();
System.out.println("StringBuilder -- 总共耗时:" + (endTime - startTime) + "ms,字符串长度:" + STRINGBUILDER.length());
}
}
四、总结
- 对于不可变字符串或者字符串内容改变少的情况,推荐 String。
- 字符串对象可能会被多线程访问,并且经常改变内容,推荐 StringBuffer。
- 在堆栈封闭等线程安全的环境下(方法中的局部变量)推荐 StringBuilder。