浅谈 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。
相关文章
|
10天前
|
安全 Java
String、StringBuffer、StringBuilder的区别
这篇文章讨论了Java中String、StringBuffer和StringBuilder的区别。String是不可变的,每次操作都会产生新的对象,效率低且浪费内存。StringBuilder可以在原字符串基础上进行操作,不开辟额外内存,弥补了String的缺陷。StringBuffer和StringBuilder类似,但StringBuffer的方法是线程安全的。文章还列举了StringBuffer的常用方法,并提供了使用示例代码。最后总结了这三者的主要区别。
String、StringBuffer、StringBuilder的区别
|
18天前
|
缓存 Java 应用服务中间件
Java虚拟线程探究与性能解析
本文主要介绍了阿里云在Java-虚拟-线程任务中的新进展和技术细节。
|
19天前
|
安全 Java
Java StringBuffer 和 StringBuilder 类详解
在 Java 中,`StringBuffer` 和 `StringBuilder` 用于操作可变字符串,支持拼接、插入、删除等功能。两者的主要区别在于线程安全性和性能:`StringBuffer` 线程安全但较慢,适用于多线程环境;`StringBuilder` 非线程安全但更快,适合单线程环境。选择合适的类取决于具体的应用场景和性能需求。通常,在不需要线程安全的情况下,推荐使用 `StringBuilder` 以获得更好的性能。
|
2月前
|
API C# 开发者
WPF图形绘制大师指南:GDI+与Direct2D完美融合,带你玩转高性能图形处理秘籍!
【8月更文挑战第31天】GDI+与Direct2D的结合为WPF图形绘制提供了强大的工具集。通过合理地使用这两种技术,开发者可以创造出性能优异且视觉效果丰富的WPF应用程序。在实际应用中,开发者应根据项目需求和技术背景,权衡利弊,选择最合适的技术方案。
51 0
|
2月前
|
存储 监控 Java
Java多线程优化:提高线程池性能的技巧与实践
Java多线程优化:提高线程池性能的技巧与实践
65 1
|
9天前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
16天前
|
Java Spring
spring多线程实现+合理设置最大线程数和核心线程数
本文介绍了手动设置线程池时的最大线程数和核心线程数配置方法,建议根据CPU核数及程序类型(CPU密集型或IO密集型)来合理设定。对于IO密集型,核心线程数设为CPU核数的两倍;CPU密集型则设为CPU核数加一。此外,还讨论了`maxPoolSize`、`keepAliveTime`、`allowCoreThreadTimeout`和`queueCapacity`等参数的设置策略,以确保线程池高效稳定运行。
83 10
spring多线程实现+合理设置最大线程数和核心线程数
|
25天前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
45 15
一个Android App最少有几个线程?实现多线程的方式有哪些?
|
7天前
|
NoSQL 网络协议 Unix
1)Redis 属于单线程还是多线程?不同版本之间有什么区别?
1)Redis 属于单线程还是多线程?不同版本之间有什么区别?
20 1
|
10天前
|
Python
5-5|python开启多线程入口必须在main,从python线程(而不是main线程)启动pyQt线程有什么坏处?...
5-5|python开启多线程入口必须在main,从python线程(而不是main线程)启动pyQt线程有什么坏处?...