recorrer net how java multithreading thread-safety stringbuilder

java - net - stringbuilder vba excel



¿Cómo compruebo programáticamente que StringBuilder no es seguro para subprocesos? (3)

Problema

Me temo que la prueba que has escrito es incorrecta.

El principal requisito es compartir la misma instancia de StringBuilder entre diferentes hilos. Mientras que usted está creando un objeto StringBuilder para cada hilo.

El problema es que un new Threadsafe() inicializa un new StringBuilder() :

class Threadsafe { ... StringBuilder sb = new StringBuilder(str); ... } class MyThread1 implements Runnable { Threadsafe sf = new Threadsafe(); ... } class MyThread2 implements Runnable { Threadsafe sf = new Threadsafe(); ... }

Explicación

Para probar que la clase StringBuilder no es segura para subprocesos, debe escribir una prueba donde n subprocesos ( n > 1 ) agreguen algunas cosas a la misma instancia simultáneamente.

Al tener en cuenta el tamaño de todas las cosas que va a agregar, podrá comparar este valor con el resultado de builder.toString().length() :

final long SIZE = 1000; // max stream size final StringBuilder builder = Stream .generate(() -> "a") // generate an infinite stream of "a" .limit(SIZE) // make it finite .parallel() // make it parallel .reduce(new StringBuilder(), StringBuilder::append, (b1, b2) -> b1); // put each element in the builder Assert.assertEquals(SIZE, builder.toString().length());

Como en realidad no es seguro para subprocesos, es posible que tenga problemas para obtener el resultado.

Se puede ArrayIndexOutOfBoundsException una ArrayIndexOutOfBoundsException debido a la matriz de char[] AbstractStringBuilder#value y al mecanismo de asignación que no fue diseñado para el uso de subprocesos múltiples.

Prueba

Aquí está mi prueba JUnit 5 que cubre tanto StringBuilder como StringBuffer :

public class AbstractStringBuilderTest { @RepeatedTest(10000) public void testStringBuilder() { testAbstractStringBuilder(new StringBuilder(), StringBuilder::append); } @RepeatedTest(10000) public void testStringBuffer() { testAbstractStringBuilder(new StringBuffer(), StringBuffer::append); } private <T extends CharSequence> void testAbstractStringBuilder(T builder, BiFunction<T, ? super String, T> accumulator) { final long SIZE = 1000; final Supplier<String> GENERATOR = () -> "a"; final CharSequence sequence = Stream .generate(GENERATOR) .parallel() .limit(SIZE) .reduce(builder, accumulator, (b1, b2) -> b1); Assertions.assertEquals( SIZE * GENERATOR.get().length(), // expected sequence.toString().length() // actual ); } }

Resultados

AbstractStringBuilderTest.testStringBuilder: 10000 total, 165 error, 5988 failed, 3847 passed. AbstractStringBuilderTest.testStringBuffer: 10000 total, 10000 passed.

¿Cómo compruebo programáticamente que StringBuilder no es seguro para subprocesos?

Intenté esto, pero no está funcionando:

public class Threadsafe { public static void main(String[] args) throws InterruptedException { long startdate = System.currentTimeMillis(); MyThread1 mt1 = new MyThread1(); Thread t = new Thread(mt1); MyThread2 mt2 = new MyThread2(); Thread t0 = new Thread(mt2); t.start(); t0.start(); t.join(); t0.join(); long enddate = System.currentTimeMillis(); long time = enddate - startdate; System.out.println(time); } String str = "aamir"; StringBuilder sb = new StringBuilder(str); public void updateme() { sb.deleteCharAt(2); System.out.println(sb.toString()); } public void displayme() { sb.append("b"); System.out.println(sb.toString()); } } class MyThread1 implements Runnable { Threadsafe sf = new Threadsafe(); public void run() { sf.updateme(); } } class MyThread2 implements Runnable { Threadsafe sf = new Threadsafe(); public void run() { sf.displayme(); } }


Considere la siguiente prueba.

import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.junit.Assert; import org.junit.Before; import org.junit.Test; public class NotThreadSafe { private static final int CHARS_PER_THREAD = 1_000_000; private static final int NUMBER_OF_THREADS = 4; private StringBuilder builder; @Before public void setUp() { builder = new StringBuilder(); } @Test public void testStringBuilder() throws ExecutionException, InterruptedException { Runnable appender = () -> { for (int i = 0; i < CHARS_PER_THREAD; i++) { builder.append(''A''); } }; ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_OF_THREADS); List<Future<?>> futures = new ArrayList<>(); for (int i = 0; i < NUMBER_OF_THREADS; i++) { futures.add(executorService.submit(appender)); } for (Future<?> future : futures) { future.get(); } executorService.shutdown(); String builtString = builder.toString(); Assert.assertEquals(CHARS_PER_THREAD * NUMBER_OF_THREADS, builtString.length()); } }

Con esto se pretende demostrar que StringBuilder no es seguro para subprocesos mediante la prueba mediante el método de contradicción . Cuando se ejecuta, siempre lanza una excepción como la siguiente:

java.util.concurrent.ExecutionException: java.lang.ArrayIndexOutOfBoundsException: 73726 at java.util.concurrent.FutureTask.report(FutureTask.java:122) at java.util.concurrent.FutureTask.get(FutureTask.java:192) at NotThreadSafe.testStringBuilder(NotThreadSafe.java:37) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) Caused by: java.lang.ArrayIndexOutOfBoundsException: 73726 at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:650) at java.lang.StringBuilder.append(StringBuilder.java:202) at NotThreadSafe.lambda$testStringBuilder$0(NotThreadSafe.java:28) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)

Por lo tanto, StringBuilder se rompe cuando es usado por múltiples hilos.


Mucho más simple:

StringBuilder sb = new StringBuilder(); IntStream.range(0, 10) .parallel() .peek(sb::append) // don''t do this! just to prove a point... .boxed() .collect(Collectors.toList()); if (sb.toString().length() != 10) { System.out.println(sb.toString()); }

No habrá orden de los dígitos (no serán 012... y así sucesivamente), pero esto es algo que no te importa. Todo lo que te importa es que no todos los dígitos del rango [0..10] agregaron a StringBuilder .

Por otro lado, si reemplaza StringBuilder con StringBuffer , siempre obtendrá 10 elementos en ese búfer (pero fuera de orden).