gpt4 book ai didi

java - 多线程修改StringBuilder

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:57:09 26 4
gpt4 key购买 nike

我问的问题与Difference between StringBuilder and StringBuffer有关但不一样。我想看看如果 StringBuilder 同时被两个线程修改会发生什么。

我编写了以下类:

public class ThreadTester
{
public static void main(String[] args) throws InterruptedException
{
Runnable threadJob = new MyRunnable();
Thread myThread = new Thread(threadJob);
myThread.start();

for (int i = 0; i < 100; i++)
{
Thread.sleep(10);
StringContainer.addToSb("a");
}

System.out.println("1: " + StringContainer.getSb());
System.out.println("1 length: " + StringContainer.getSb().length());
}
}

public class MyRunnable implements Runnable
{
@Override
public void run()
{
for (int i = 0; i < 100; i++)
{
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
StringContainer.addToSb("b");
}

System.out.println("2: " + StringContainer.getSb());
System.out.println("2 length: " + StringContainer.getSb().length());
}
}

public class StringContainer
{
private static final StringBuffer sb = new StringBuffer();

public static StringBuffer getSb()
{
return sb;
}

public static void addToSb(String s)
{
sb.append(s);
}
}

最初我在 StringContainer 中保留了一个 StringBuffer。由于 StringBuffer 是线程安全的,一次只有一个线程可以附加到它,所以输出是一致的——要么两个线程都报告缓冲区的长度为 200,如:

1: abababababababababbaabababababababbaababababababababababababbabaabbababaabbaababababbababaabbababaabababbaabababbababababaababababababababbababaabbaababbaababababababbaababbababaababbabaabbababababaab
1 length: 200
2: abababababababababbaabababababababbaababababababababababababbabaabbababaabbaababababbababaabbababaabababbaabababbababababaababababababababbababaabbaababbaababababababbaababbababaababbabaabbababababaab
2 length: 200

或者其中一个报告 199 而另一个报告 200,例如:

2: abbabababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab
2 length: 199
1: abbababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababa
1 length: 200

关键是最后一个完成的线程报告长度为 200。

现在,我将 StringContainer 更改为具有 StringBuilder 而不是 StringBuffer 即

public class StringContainer
{
private static final StringBuilder sb = new StringBuilder();

public static StringBuilder getSb()
{
return sb;
}

public static void addToSb(String s)
{
sb.append(s);
}
}

我预计一些写入会被覆盖,这种情况正在发生。但是 StringBuilder 的内容和长度有时不匹配:

1: ababbabababaababbaabbabababababaab
1 length: 137
2: ababbabababaababbaabbabababababaab
2 length: 137

可以看到打印出来的内容只有34个字符,但是长度是137,这是为什么?

@Extreme Coders - 我刚刚又做了一次测试:

2: ababbabababaabbababaabbababaababaabbaababbaaababbaabbabbabbabababbabababbbabbbbbabababbaabababbabaabaaabaababbaabaababababbaabbbabbbbbababababbababaab
1: ababbabababaabbababaabbababaababaabbaababbaaababbaabbabbabbabababbabababbbabbbbbabababbaabababbabaabaaabaababbaabaababababbaabbbabbbbbababababbababaab
1 length: 150
2 length: 150

Java 版本:1.6.0_45 我使用的是 eclipse 版本:面向 Web 开发人员的 Eclipse Java EE IDE。版本:Juno Service Release 2构建 ID:20130225-0426

更新 1: 我在 eclipse 外运行了这个,现在它们似乎匹配,但有时我会收到 ArrayIndexOutOfBoundsException:

$ java -version
java version "1.6.0_27"
OpenJDK Runtime Environment (IcedTea6 1.12.5) (6b27-1.12.5-0ubuntu0.12.04.1)
OpenJDK Server VM (build 20.0-b12, mixed mode)

$ java ThreadTester
1: ababbbbbabbabababababaababbaabbbaabababbbababbabababbabbababbbbbbabaabaababbbbbbabbbbbaabbaaabbbbaabbbababababbbbabbababab
1 length: 123
2: ababbbbbabbabababababaababbaabbbaabababbbababbabababbabbababbbbbbabaabaababbbbbbabbbbbaabbaaabbbbaabbbababababbbbabbababab
2 length: 123

$ java ThreadTester
2: abbabaabbbbbbbbbababbbbbabbbabbbabaaabbbbbbbabababbbbbbbbbabbbbbbbababababbabbbbaabbbaaabbabaaababaaaabaabbaabbbb
2 length: 115
1: abbabaabbbbbbbbbababbbbbabbbabbbabaaabbbbbbbabababbbbbbbbbabbbbbbbababababbabbbbaabbbaaabbabaaababaaaabaabbaabbbb
1 length: 115

$ java ThreadTester
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
at java.lang.System.arraycopy(Native Method)
at java.lang.String.getChars(String.java:862)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:408)
at java.lang.StringBuilder.append(StringBuilder.java:136)
at StringContainer.addToSb(StringContainer.java:14)
at ThreadTester.main(ThreadTester.java:14)
2: abbbbbbababbbbabbbbababbbbaabbabbbaaabbbababbbbabaabaabaabaaabababaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
2 length: 114

从 eclipse 运行时也会发生 ArrayIndexOutOfBoundsException。

更新 2:有两个问题正在发生。 StringBuilder 的内容与长度不匹配的第一个问题只发生在 Eclipse 中,而不是当我在命令行中运行时(至少我在命令行中运行它 100 多次它从未发生过)。

ArrayIndexOutOfBoundsException 的第二个问题应该与 StringBuilder 类的内部实现有关,它保留一个字符数组并在扩展大小时执行 Arrays.copyOf。但无论执行顺序如何,在扩展大小之前写入是如何发生的仍然让我感到震惊。

顺便说一句,我倾向于同意@GreyBeardedGeek 的回答,即整个练习是在浪费大量时间:-)。有时我们只能看到症状,即某些代码的输出,并想知道出了什么问题。这个问题声明先验两个线程正在修改一个(众所周知的)线程不安全对象。

更新 3: 这是来自 Java Concurrency in Practice 的官方答案p. 35:

  • 在没有同步的情况下,编译器、处理器和运行时可以按照其中的顺序做一些非常奇怪的事情操作似乎在执行。尝试对顺序进行推理哪些内存操作“必须”发生在不充分同步的情况下多线程程序几乎肯定是不正确的。

  • 关于并发程序同步不足的原因是非常困难。

p 上的书中还有一个很好的示例NoVisibility。 34.

最佳答案

当多个线程并发访问时,非线程安全类的行为根据定义是“未定义的”。

恕我直言,在这种情况下任何确定确定性行为的尝试都是 只是浪费大量时间。

关于java - 多线程修改StringBuilder,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17003180/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com