简体   繁体   English

Thread.sleep阻止其他线程

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM