17

Разница между StringBuilder и StringBuffer

14

В чем основное отличие между StringBuffer и StringBuilder? Существуют ли проблемы с производительностью при выборе одного из этих классов?

5 ответ(ов)

1

Просто используйте StringBuilder, если вам действительно не нужно делиться буфером между потоками. StringBuilder – это несинхронизированная (меньше накладных расходов = более эффективная) версия оригинального синхронизированного класса StringBuffer.

StringBuffer появился первым. Sun заботилась о корректности во всех условиях, поэтому они сделали его синхронизированным, чтобы обеспечить безопасность при работе с потоками.

StringBuilder был представлен позже. Большинство случаев использования StringBuffer было в однопоточных приложениях, и это привело к ненужным затратам на синхронизацию.

Поскольку StringBuilder является заменой для StringBuffer, но без синхронизации, не будет никаких различий между примерами использования этих классов.

Если же вам действительно нужно делиться данными между потоками, вы можете использовать StringBuffer, но подумайте, не будет ли целесообразнее использовать более высокоуровневую синхронизацию. Например, возможно, вместо использования StringBuffer стоит синхронизировать методы, использующие StringBuilder.

0

Сначала давайте рассмотрим сходства: Как StringBuilder, так и StringBuffer являются изменяемыми объектами. Это означает, что вы можете изменять их содержимое в том же месте.

Различия: StringBuffer является изменяемым и синхронизированным, в то время как StringBuilder изменяемый, но по умолчанию не синхронизирован.

Что значит синхронизированный (синхронизация): Когда что-то является синхронизированным, это значит, что несколько потоков могут одновременно получать доступ и изменять его без каких-либо проблем или побочных эффектов. StringBuffer синхронизирован, поэтому вы можете использовать его с несколькими потоками без каких-либо проблем.

Когда использовать что? StringBuilder: Когда вам нужна изменяемая строка, и только один поток имеет доступ к ней и модифицирует её. StringBuffer: Когда вам нужна изменяемая строка, и несколько потоков получают доступ к ней и модифицируют её.

Примечание: Не используйте StringBuffer без необходимости, то есть, не используйте его, если только один поток изменяет и получает доступ к строке, поскольку он содержит много кода для блокировки и разблокировки для синхронизации, что ненужно расходует ресурсы ЦП. Не используйте блокировки, если это не требуется.

0

В однопоточных приложениях StringBuffer не значительно медленнее, чем StringBuilder, благодаря оптимизациям JVM. Но в многопоточном режиме безопасно использовать только StringBuffer.

Вот мой тест (это не бенчмарк, просто тест):

public static void main(String[] args) {
    String withString = "";
    long t0 = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        withString += "some string";
    }
    System.out.println("strings: " + (System.currentTimeMillis() - t0));

    t0 = System.currentTimeMillis();
    StringBuffer buf = new StringBuffer();
    for (int i = 0; i < 100000; i++) {
        buf.append("some string");
    }
    System.out.println("Buffers: " + (System.currentTimeMillis() - t0));

    t0 = System.currentTimeMillis();
    StringBuilder building = new StringBuilder();
    for (int i = 0; i < 100000; i++) {
        building.append("some string");
    }
    System.out.println("Builder: " + (System.currentTimeMillis() - t0));
}

Результаты:

  • strings: 319740
  • Buffers: 23
  • Builder: 7!

Таким образом, StringBuilder быстрее StringBuffer и значительно быстрее, чем конкатенация строк. Теперь давайте используем Executor для многопоточности:

public class StringsPerf {

    public static void main(String[] args) {
        ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
        
        // С использованием StringBuffer
        StringBuffer buffer = new StringBuffer();
        for (int i = 0; i < 10; i++) {
            executorService.execute(new AppendableRunnable(buffer));
        }
        shutdownAndAwaitTermination(executorService);
        System.out.println(" Thread Buffer: " + AppendableRunnable.time);

        // С использованием StringBuilder
        AppendableRunnable.time = 0;
        executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < 10; i++) {
            executorService.execute(new AppendableRunnable(builder));
        }
        shutdownAndAwaitTermination(executorService);
        System.out.println(" Thread Builder: " + AppendableRunnable.time);
    }

    static void shutdownAndAwaitTermination(ExecutorService pool) {
        pool.shutdown(); // сокращенный код из официальной документации по Executor
        try {
            if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
                pool.shutdownNow();
                if (!pool.awaitTermination(60, TimeUnit.SECONDS))
                    System.err.println("Pool did not terminate");
            }
        } catch (Exception e) {}
    }
}

class AppendableRunnable<T extends Appendable> implements Runnable {

    static long time = 0;
    T appendable;

    public AppendableRunnable(T appendable) {
        this.appendable = appendable;
    }

    @Override
    public void run() {
        long t0 = System.currentTimeMillis();
        for (int j = 0; j < 10000; j++) {
            try {
                appendable.append("some string");
            } catch (IOException e) {}
        }
        time += (System.currentTimeMillis() - t0);
    }
}

Теперь StringBuffer занимает 157 мс для 100000 добавлений. Это не тот же тест, но по сравнению с предыдущими 37 мс, можно безусловно утверждать, что StringBuffer медленнее в многопоточном режиме. Причина в том, что JIT/HotSpot компилятор проводит оптимизации, когда не требуется проверка блокировок.

Однако при использовании StringBuilder вы можете получить java.lang.ArrayIndexOutOfBoundsException, потому что параллельный поток пытается добавить что-то в недопустимое место.

Вывод: не стоит гнаться за StringBuffer. В многопоточных приложениях стоит подумать о том, что делают потоки, прежде чем пытаться выиграть несколько наносекунд.

0

Хороший вопрос!

Вот различия, которые я заметил:

StringBuffer:

  • StringBuffer — синхронизирован.
  • StringBuffer — безопасен для потоков (thread-safe).
  • StringBuffer медленнее (попробуйте написать тестовую программу и выполнить её, время выполнения будет больше, чем у StringBuilder).

StringBuilder:

  • StringBuilder — не синхронизирован.
  • StringBuilder — не безопасен для потоков (not thread-safe).
  • Производительность StringBuilder лучше, чем у StringBuffer.

Общее:

Оба класса имеют одинаковые методы с одинаковыми сигнатурами. Оба являются изменяемыми (mutable).

0
**StringBuffer**

- Синхронизирован, поэтому потокобезопасен.
- Потокобезопасен, но из-за этого медленнее.

**StringBuilder**

- Введён в Java 5.0.
- Асинхронен, следовательно, быстрее и эффективнее.
- Пользователю нужно явно синхронизировать его, если это необходимо.
- Его можно заменить на `StringBuffer` без каких-либо других изменений.
Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь