`Thread.sleep()`会停止调用线程的`run`方法吗?

huangapple go评论78阅读模式
英文:

Does Thread.sleep() stops calling threads run method?

问题

以下是您提供的代码的翻译部分:

请问有人可以解释一下我漏掉了什么吗当我调用`Thread.sleep(1000)`我认为两个线程都应在1秒内执行所以之后为什么我要将`doSlice`设置为false在1秒内停止线程为什么`Thread.sleep()`不能在1秒内停止它们我的意思是经过1秒的运行后甚至不应调用运行方法来检查while条件

public class ExecutionScheduling extends Thread {
    public int slice_count = 0;
    public boolean doSlice = true;
    public String name;

    public ExecutionScheduling(String name) {
        this.name = name;
    }

    public void run() {
        while (doSlice) {
            slice_count++;
            System.out.println(name);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutionScheduling executionScheduling = new ExecutionScheduling("ex");
        ExecutionScheduling executionScheduling1 = new ExecutionScheduling("ex1");

        executionScheduling.start();
        executionScheduling1.start();

        Thread.sleep(1000);
        executionScheduling.doSlice = false;
        executionScheduling1.doSlice = false;

        System.out.println("ex: " + executionScheduling.slice_count);
        System.out.println("ex1: " + executionScheduling1.slice_count);
    }
}

请注意,由于您要求仅返回翻译的部分,因此我已将代码的其余部分忽略。如果您有任何其他问题或需要进一步的帮助,请随时提问。

英文:

Can someone please explain to me what I am missing: when I call Thread.sleep(1000) I suppose both threads should be executed in 1s so after that why should I make doSlice false to stop threads in 1s
why Thread.sleep() just doesn't stop them in 1s. I mean after 1s run method even shouldn't be called to check while condition:

public class ExecutionScheduling  extends Thread{
    public int slice_count=0;
    public boolean doSlice=true;
    public String name;

    public ExecutionScheduling(String name){
        this.name=name;
    }

    public void run() {
        while (doSlice) {
            slice_count++;
            System.out.println(name);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutionScheduling executionScheduling=new ExecutionScheduling("ex");
        ExecutionScheduling executionScheduling1=new ExecutionScheduling("ex1");

        executionScheduling.start();
        executionScheduling1.start();

        Thread.sleep(1000);
        executionScheduling.doSlice=false;
        executionScheduling1.doSlice=false;

        System.out.println("ex: "+executionScheduling.slice_count);
        System.out.println("ex1: "+executionScheduling1.slice_count);
    }
}

答案1

得分: 3

> 有人可以解释一下我缺了什么吗?

你所缺少的是线程之间的内存同步。当你启动两个后台线程时,它们拥有各自的本地内存(在各自的 CPU 上),你需要明确地更新它们之间共享的数据。

让代码变得复杂的是,System.out.println(...) 是一个同步方法,因此它在某种程度上为你提供了一些内存同步,但你不应该依赖它(见下文)。这意味着如果你移除了那段调试代码,你的程序行为将会不同。在线程代码中要小心使用任何 System.out.print*

> Thread.sleep(1000);

当你运行这个睡眠命令时,它会使主线程进入睡眠,但这两个后台线程会继续运行。它们各自更新自己的 slice_count 值,但不能保证主线程会看到这些更新。

// 需要在这里添加 volatile 关键字
private volatile int slice_count;

通过在 slice_count 上添加 volatile Java 关键字,将该字段标记为被多个线程访问。当主线程访问 slice_count 时,它将读取最新的值。你可能还希望了解一下 AtomicInteger,它包装了一个 volatile int,允许多个线程执行诸如 incrementAndGet() 之类的操作。

另一个存在内存同步问题的地方是:

executionScheduling.doSlice = false;
executionScheduling1.doSlice = false;

所以 doSlice 字段也需要是 volatile 的:

// 需要在这里添加 volatile 关键字
public volatile boolean doSlice = true;

最后,无特定顺序:

  • 如果可能的话,你的字段应该是 private 的。

  • 应为 executionScheduling1executionScheduling2

  • 定义一个 Runnable 而不是一个 Thread 是一个更好的模式。参见:https://stackoverflow.com/a/541527/179850

  • 你可以考虑在打印结果之前使用 join() 等待每个线程完成它们的工作。

    // 将 doSlices 设置为 false
    executionScheduling.join()
    executionScheduling1.join()
    // 打印结果
    

    如果添加了 join() 调用,那么这将处理关于 slice_count 的内存同步,因此在 join() 调用完成之后访问它们时不再需要是 volatile 的。是的,这很令人困惑。线程编程是复杂的。你仍然需要将 doSlice 字段设置为 volatile,因为它们在 join() 完成之前被访问。

英文:

> Can someone please explain to me what I am missing

What you are missing is memory synchronization between the threads. When you start your 2 background threads, they have their own local memory (on their own CPUs) and you need to specifically update any shared data between them.

What is complicating the code is that System.out.println(...) is a synchronized method so it is giving you some memory synchronization "for free" but you shouldn't depend on it (see below). This means that if you removed that debug code, your program is going to be behave differently. Be careful of any usage of System.out.print* in threaded code.

> Thread.sleep(1000);

When you run this sleep command, it will cause the main thread to go to sleep but the 2 background threads continue to run. They are each updating their own copy of slice_count but there is no guarantee that the main thread will see these updates.

// need to add the volatile keyword here
private volatile int slice_count;

By adding the volatile Java keyword to slice_count, this marks the field as being accessed by multiple threads. When the main thread the accesses slice_count, it will read the most updated value of it. You might also want to look into AtomicInteger which wraps a volatile int but allows multiple threads to do stuff like incrementAndGet().

Another place where you have memory sync issues is:

executionScheduling.doSlice = false;
executionScheduling1.doSlice = false;

So the doSlice field also needs to be volatile:

// need to add the volatile keyword here
public volatile boolean doSlice = true;

And lastly, in no order:

  • Your fields should be private if at all possible.

  • It should be executionScheduling1 and executionScheduling2.

  • It is a better pattern to define a Runnable instead of a Thread. See: https://stackoverflow.com/a/541527/179850

  • You might consider doing a join() to wait or each of the threads to finish their work before you print out their results.

    // set the doSlices to false
    executionScheduling.join()
    executionScheduling1.join()
    // printf results
    

    If you add the join() calls then this will handle the memory synchronization for you in terms of slice_count so those no longer need to be volatile as long as you access them after the join() calls finish. Yes this is confusing. Thread coding is non-trivial. You will still need the doSlice fields to be volatile because they are accessed before join() finishes.

huangapple
  • 本文由 发表于 2020年7月23日 20:20:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/63054135.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定