[英]Thread.sleep blocks other Thread
I have a Output class which just prints everything that it gets to print. 我有一个Output类,它仅打印将要打印的所有内容。
public class Output {
private static List<String> textList = new ArrayList<>();
private static Output output = null;
private Output() {
Runnable task = () -> {
int lastIndex = 0;
while (true) {
while (lastIndex < textList.size()) {
System.out.println(lastIndex + " - " + textList.size() + ": " + textList.get(lastIndex));
outputText(textList.get(lastIndex));
lastIndex ++;
}
}
};
new Thread(task).start();
}
private static void outputText(String text) {
synchronized (System.out) {
System.out.println(text);
}
}
public static void say(String text) {
if (output == null) {
output = new Output();
}
textList.add(text);
}
}
When I add something to print, everything works fine: 当我添加打印内容时,一切正常:
for (int i = 0; i < 10; i++) {
Output.say("" + i);
}
But when I add a Thread.sleep
to the loop it stops on the first output: 但是,当我将
Thread.sleep
添加到循环中时,它将在第一个输出上停止:
for (int i = 0; i < 10; i++) {
Output.say("" + i);
Thread.sleep(100);
}
How can I prevent it? 我该如何预防? I mean, I'm stopping with sleep just the main thread and not the separate thread.
我的意思是,我将仅在主线程而不是单独的线程处于休眠状态。
When you don't synchronize threads correctly, there is no guaranty that threads see updates made by other threads. 如果您没有正确同步线程,则无法保证线程会看到其他线程所做的更新。 They may either completely miss updates or see only parts of them, creating an entirely inconsistent result.
他们要么完全错过更新,要么只看到其中的一部分,从而造成完全不一致的结果。 Sometimes they may even appear to do the right thing.
有时他们甚至看起来似乎在做正确的事情。 Without proper synchronization (in the sense of any valid construct specified to be thread safe), this is entirely unpredictable.
如果没有适当的同步(就任何指定为线程安全的有效构造而言),这是完全不可预测的。
Sometimes, the chances of seeing a particular behavior are higher, like in your example. 有时,像您的示例一样,看到特定行为的几率更高。 In most runs, the loop without
sleep
will complete before the other thread even starts its work, whereas inserting sleep
raises the chance of lost updates after the second thread has seen values. 在大多数运行中,没有
sleep
的循环将在另一个线程甚至开始工作之前完成,而插入sleep
会增加第二个线程看到值后丢失更新的机会。 Once the second thread has seen a value for textList.size()
, it might reuse the value forever, evaluating lastIndex < textList.size()
to false
and executing the equivalent of while(true) { }
. 一旦第二个线程看到了
textList.size()
的值,它可能会永远重复使用该值,将lastIndex < textList.size()
评估为false
并执行while(true) { }
的等效操作。
It's funny that the only place where you inserted a construct for thread safety, is the method outputText
that is called by a single thread only (and printing to System.out
is synchronized internally in most environments anyway). 有趣的是,为确保线程安全而插入结构的唯一位置是仅由单个线程调用的
outputText
方法(无论如何,在大多数环境中,打印到System.out
都是内部同步的)。
Besides, it's not clear why you are creating an object of type Output
that has no relevance here, as all fields and methods are static
. 此外,由于所有字段和方法都是
static
,因此尚不清楚为什么要创建与此处不相关的Output
类型的对象。
Your code can be corrected and simplified to 您的代码可以更正并简化为
public static void main(String[] args) throws InterruptedException {
List<String> textList = new ArrayList<>();
new Thread( () -> {
int index=0;
while(true) synchronized(textList) {
for(; index<textList.size(); index++)
System.out.println(textList.get(index));
}
}).start();
for (int i = 0; i < 10; i++) {
synchronized(textList) {
textList.add(""+i);
}
Thread.sleep(100);
}
}
though it still contains the issues of you original code of never terminating due to the infinite second thread and also burning the CPU with a polling loop. 尽管它仍然包含您的原始代码的问题,这些代码永远不会由于无限的第二个线程而终止,并且还会通过轮询循环来烧毁CPU。 You should let the second thread wait for new items and add a termination condition:
您应该让第二个线程等待新项并添加终止条件:
public static void main(String[] args) throws InterruptedException {
List<String> textList = new ArrayList<>();
new Thread( () -> {
synchronized(textList) {
for(int index=0; ; index++) {
while(index>=textList.size()) try {
textList.wait();
} catch(InterruptedException ex) { return; }
final String item = textList.get(index);
if(item==null) break;
System.out.println(item);
}
}
}).start();
for (int i = 0; i < 10; i++) {
synchronized(textList) {
textList.add(""+i);
textList.notify();
}
Thread.sleep(100);
}
synchronized(textList) {
textList.add(null);
textList.notify();
}
}
This is still only an academic example that you shouldn't use in real life code. 这仍然只是一个学术示例,您不应该在现实生活中使用它。 There are classes for thread safe data exchange provided by the Java API removing the burden of implementing such things yourself.
Java API提供了用于线程安全的数据交换的类,从而减轻了您自己实现此类事情的负担。
public static void main(String[] args) throws InterruptedException {
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
String endMarker = "END-OF-QUEUE"; // the queue does not allow null
new Thread( () -> {
for(;;) try {
String item = queue.take();
if(item == endMarker) break;// don't use == for ordinary strings
System.out.println(item);
} catch(InterruptedException ex) { return; }
}).start();
for (int i = 0; i < 10; i++) {
queue.put(""+i);
Thread.sleep(100);
}
queue.put(endMarker);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.