浅谈 String StringBuilder StringBuffer 之性能和线程安全

简介: 浅谈 String StringBuilder StringBuffer 之性能和线程安全

一、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。
相关文章
|
1月前
|
安全
String、StringBuffer、StringBuilder的区别
String 由 char[] 数组构成,使用了 final 修饰,对 String 进行改变时每次都会新生成一个 String 对象,然后把指针指向新的引用对象。 StringBuffer可变并且线程安全;有一定缓冲区容量,字符串大小没超过容量,不会重新分配新的容量,适合多线程操作字符串; StringBuiler可变并且线程不安全。速度比StringBuffer更快,适合单线程操作字符串。 操作少量字符数据用 String;单线程操作大量数据用 StringBuilder;多线程操作大量数据用 StringBuffer
|
2月前
|
canal 安全 索引
(StringBuffer和StringBuilder)以及回文串,字符串经典习题
(StringBuffer和StringBuilder)以及回文串,字符串经典习题
41 5
|
2月前
|
存储 安全 Java
String、StringBuffer 和 StringBuilder 的区别
【10月更文挑战第21天】String、StringBuffer 和 StringBuilder 都有各自的特点和适用场景。了解它们之间的区别,可以帮助我们在编程中更合理地选择和使用这些类,从而提高程序的性能和质量。还可以结合具体的代码示例和实际应用场景,进一步深入分析它们的性能差异和使用技巧,使对它们的理解更加全面和深入。
43 0
|
2月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
58 1
C++ 多线程之初识多线程
|
2月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
27 3
|
2月前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
23 2
|
2月前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
38 2
|
2月前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
45 1
|
2月前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
50 1
|
2月前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
31 1