英文:
Why does java Thread execute after the independent code?
问题
以下是您要翻译的内容:
我对Java和学习多线程还很新。我编写了以下代码。
class BackgroundTask implements Runnable {
private int counter = 0;
public int getCounter() {
return counter;
}
public void setCounter(int counter) {
this.counter = counter;
}
@Override
public void run() {
System.out.println("线程已启动");
while (true) {
this.setCounter(this.getCounter() + 1);
}
}
}
public class Main {
public static void main(String[] args) {
BackgroundTask bgTask = new BackgroundTask();
Thread bgCount = new Thread(bgTask);
try {
bgCount.start();
System.out.println("后台计数正在运行");
bgCount.interrupt();
System.out.println(bgTask.getCounter());
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
代码的输出:
后台计数正在运行
0
线程已启动
问题1:为什么bgCount.start()
在写在打印语句之前,但在打印语句之后执行?
问题2:为什么在调用getCounter()
方法后线程才开始运行?
编辑:感谢大家提供的所有精彩答案,现在我理解了线程的概念。
英文:
I am new to java and learning multi-threading. I wrote the following code.
class BackgroundTask implements Runnable {
private int counter = 0;
public int getCounter() {
return counter;
}
public void setCounter(int counter) {
this.counter = counter;
}
@Override
public void run() {
System.out.println("Thread started");
while (true) {
this.setCounter(this.getCounter() + 1);
}
}
}
public class Main {
public static void main(String[] args) {
BackgroundTask bgTask = new BackgroundTask();
Thread bgCount = new Thread(bgTask);
try {
bgCount.start();
System.out.println("counter in background is running");
bgCount.interrupt();
System.out.println(bgTask.getCounter());
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
Output of the code:
counter in background is running
0
Thread started
Q.1 why is bgCount.start()
executing after the print statement when it is written before it?
Q.2 Why did the thread start after calling the getCounter()
method?
Edit: Thanks everyone for all the cool answers, now I understand the concept of threads.
答案1
得分: 3
当你在两个不同的线程中执行任务时,它们是不同步的,除非你强制进行某种形式的同步。一个线程可能会先执行,或者另一个线程可能会先执行,它们也可能以不可预测的方式交错执行。这实际上就是线程的用意所在。
英文:
When you do things in two different threads, they are unsynchronized unless you force some kind of synchronization. One thread might execute first or the other thread might or they might interleave in unpredictable ways. That's kind of the point of threads.
答案2
得分: 2
> Q.1 为什么 bgCount.start()
在打印语句之后执行,而不是在之前执行?
这不是正确的结论。start()
正是在您编写的位置执行的。它只是不一定意味着线程立即启动。操作系统调度程序决定何时启动线程。您唯一得到的保证是它最终会启动,可能是现在,可能是一个小时后,可能是明年。在实践中,通常几乎会瞬间启动。但这并不意味着它会作为紧随其后的下一件事情被执行。
> Q.2 为什么在调用 getCounter()
方法后线程才启动?
如前所述,这是操作系统调度程序决定的。您只是运气不好。认真说,您对此没有控制,并且不应该对此做任何假设。
多线程
如果您有多个线程,并且它们有一系列操作要执行,操作系统调度程序完全可以自由决定如何交错执行这些操作。这也意味着不交错执行任何操作也是有效的。
让我们看一个带有线程 A
和 B
的示例,它们各自有两个操作要执行。以下是调度程序的有效执行顺序:
A.1
A.2
B.1
B.2
A.1
B.1
A.2
B.2
A.1
B.1
B.2
A.2
B.1
B.2
A.1
A.2
B.1
A.1
B.2
A.2
B.1
A.1
A.2
B.2
因此,您不应该对线程何时启动以及特别是在与其他线程相比操作的执行顺序方面做任何假设。可能会完全交错,可能是顺序执行,可能是部分交错,一切都可能发生。
如果您想控制机制,正确的工具是同步。通过这种方式,您可以告诉线程在继续之前要等待另一个线程中发生的某事。对于您上面的代码,一个非常简单的示例是等待 bgCount
完全完成后再继续打印计数。您可以使用 join()
来实现:
bgCount.start();
System.out.println("后台计数正在运行");
bgCount.join(); // 等待完成
System.out.println(bgTask.getCounter());
但是,如果您这样做,那就失去了使用线程的目的。如果您完全阻塞另一个线程并等待,那么与普通的顺序方式执行相比,并行计算没有任何好处。
英文:
Explanation
> Q.1 why is bgCount.start()
executing after the print statement when it is written before it?
It is not, this is an incorrect conclusion. start()
is executed exactly when you have written it. It just does not necessarily mean that the thread immediatly starts. The OS scheduler determines when to start a thread. The only guarantee that you get is that it will eventually start, so maybe now, maybe in a hour, maybe next year. Obviously, in practice it will usually be almost instant. But that does not mean it is executed as the very next thing.
> Q.2 Why did the thread start after calling the getCounter()
method?
As explained before, the OS scheduler decides. You just had bad luck. Seriously, you have no control over this and should not do any assumptions on this.
Multi-threading
If you have multiple threads and they have a series of operations to execute, the OS scheduler is completely free to decide how to interleave the operations. That also means that not interleaving anything is valid as well.
Lets take a look at an example with thread A
and B
that have 2 operations to execute each. The following orders of executions are all valid outcomes of the scheduler:
A.1
A.2
B.1
B.2
A.1
B.1
A.2
B.2
A.1
B.1
B.2
A.2
B.1
B.2
A.1
A.2
B.1
A.1
B.2
A.2
B.1
A.1
A.2
B.2
So you must not make any assumptions about when a thread starts and especially not about in which order operations are executed in regards to other threads. It might be fully interleaved, might be sequential, might be partially interleaved, everything could happen.
If you want to take control over the mechanism, the correct tool is synchronization. With that you can tell that you want to wait for a certain thing to happen first in another thread before you continue. A very simple example for your above code would be to wait until bgCount
is fully done before continuing to print the count. You can do so by using join()
:
bgCount.start();
System.out.println("counter in background is running");
bgCount.join(); // waiting until down
System.out.println(bgTask.getCounter());
However, if you do it like that, you defeated the purpose of having a thread in the first place. There is no benefit in computing something in-parallel if you completely block the other thread and wait. Then it is basically just like executing something in the ordinary sequential way.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论