英文:
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
的。 -
应为
executionScheduling1
和executionScheduling2
。 -
定义一个
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
andexecutionScheduling2
. -
It is a better pattern to define a
Runnable
instead of aThread
. 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 ofslice_count
so those no longer need to bevolatile
as long as you access them after thejoin()
calls finish. Yes this is confusing. Thread coding is non-trivial. You will still need thedoSlice
fields to bevolatile
because they are accessed beforejoin()
finishes.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论