java - 为什么这些线程没有有序地开始和结束?如何解决?
问题描述
我是Java乘法线程的新手,我坚持研究我从教程中找到的练习:
想象一下这个场景:我有一个100hp的gareen,它被攻击了10次,我的代码是:
public class Hero{
public String name;
public float hp;
public int damage;
public Hero(){
}
public Hero(String name,float hp,int damage){
this.name = name;
this.hp = hp;
this.damage = damage;
}
public synchronized void hurt(){
this.hp -= 1;
}
public void recover(){
this.hp += 1;
}
public void attackHero(Hero h) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
h.hp-=damage;
System.out.format("%s attacked %s, %s'hp become %.0f%n",name,h.name,h.name,h.hp);
if(h.isDead())
System.out.println(h.name +"dead!");
}
public boolean isDead() {
return 0>=hp?true:false;
}
}
public class TestHero {
public static void main(String[] args) {
Hero gareen = new Hero("Gareen", 100, 10);
System.out.printf("Initial HP of Gareen: %f\n",gareen.hp);
int N = 10;
Thread[] hurtThreads = new Thread[N];
Thread[] coverThreads = new Thread[N];
for (int i = 0; i < N; i++) {
Thread t1 = new Thread(() -> {
gareen.hurt();
System.out.println(Thread.currentThread().getName() + ": Gareen lost 1 HP,now his HP: " + gareen.hp);
try{
Thread.sleep(3000);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " ended");
});
t1.start();
System.out.println(t1.getName() + " started");
hurtThreads[i] = t1;
}
// In this time, all threads have been spawned and unstart
for(Thread t : hurtThreads){
try {
t.join();
System.out.println(t.getName() + " joined ");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Now, Gareen's HP: " + gareen.hp);
}
}
在我的想象中,它应该是这样的(为了便于阅读,我将 result() 隔开):
Initial HP of Gareen: 100.000000
// started and joined will be printed first because they all are sychronized code
Thread-0 started
Thread-1 started
Thread-2 started
Thread-3 started
Thread-4 started
Thread-5 started
Thread-6 started
Thread-7 started
Thread-8 started
Thread-9 started
//
Thread-0 joined
Thread-1 joined
Thread-2 joined
Thread-3 joined
Thread-4 joined
Thread-5 joined
Thread-6 joined
Thread-7 joined
Thread-8 joined
Thread-9 joined
//
// the thread will be started and ended orderly
Thread-0: Gareen lost 1 HP,now his HP: 99.0
Thread-1: Gareen lost 1 HP,now his HP: 98.0
Thread-2: Gareen lost 1 HP,now his HP: 97.0
Thread-3: Gareen lost 1 HP,now his HP: 96.0
Thread-5: Gareen lost 1 HP,now his HP: 95.0
Thread-6: Gareen lost 1 HP,now his HP: 94.0
Thread-7: Gareen lost 1 HP,now his HP: 93.0
Thread-8: Gareen lost 1 HP,now his HP: 92.0
Thread-9: Gareen lost 1 HP,now his HP: 91.0
Thread-10: Gareen lost 1 HP,now his HP: 90.0
//
Thread-0 ended
Thread-1 ended
Thread-2 ended
Thread-3 ended
Thread-4 ended
Thread-5 ended
Thread-6 ended
Thread-7 ended
Thread-8 ended
Thread-9 ended
//
Now, Gareen's HP: 90.0
但现实是(相同,为了可读性而隔开):
Initial HP of Gareen: 100.000000
Thread-0 started
Thread-1 started
Thread-2 started
Thread-3 started
Thread-4 started
Thread-5 started
Thread-6 started
Thread-7 started
Thread-8 started
Thread-9 started
Thread-3: Gareen lost 1 HP,now his HP: 96.0
Thread-7: Gareen lost 1 HP,now his HP: 92.0
Thread-2: Gareen lost 1 HP,now his HP: 97.0
Thread-4: Gareen lost 1 HP,now his HP: 95.0
Thread-1: Gareen lost 1 HP,now his HP: 98.0
Thread-6: Gareen lost 1 HP,now his HP: 93.0
Thread-0: Gareen lost 1 HP,now his HP: 99.0
Thread-9: Gareen lost 1 HP,now his HP: 90.0
Thread-8: Gareen lost 1 HP,now his HP: 91.0
Thread-5: Gareen lost 1 HP,now his HP: 94.0
Thread-3 ended
Thread-6 ended
Thread-2 ended
Thread-4 ended
Thread-1 ended
Thread-7 ended
Thread-9 ended
Thread-8 ended
Thread-0 ended
Thread-5 ended
Thread-0 joined
Thread-1 joined
Thread-2 joined
Thread-3 joined
Thread-4 joined
Thread-5 joined
Thread-6 joined
Thread-7 joined
Thread-8 joined
Thread-9 joined
Now, Gareen's HP: 90.0
我不明白为什么 join() 总是在调用线程结束之后结束,但我知道它阻塞了最后一条语句System.out.println("Now, Gareen's HP: " + gareen.hp);
以确保将在最后打印。
根据打印结果,似乎每个线程几乎在同一时间开始,但它们的做事和结束都是不同的。 另一个奇怪的部分是我发现更改后的 HP 可以正确处理诸如t0 - 99
, t2 - 98
, ...之类的线程标签t9 - 90
,但是它们没有按降序打印,我花了很长时间才意识到可能的原因: 系统打印什么消息与线程实际所做的不匹配,
我什至发现,当我删除Thread.sleep
() 时,定义的 Thread 中的两个打印语句没有按顺序运行,似乎Threads 中的代码实际上没有按顺序运行。我一直认为 Thread 只是一个不能与外部同步代码有序运行的函数。
for (int i = 0; i < N; i++) {
Thread t1 = new Thread(() -> {
gareen.hurt();
System.out.println(Thread.currentThread().getName() + ": Gareen lost 1 HP,now his HP: " + gareen.hp);
System.out.println(Thread.currentThread().getName() + " ended");
});
t1.start();
System.out.println(t1.getName() + " started");
hurtThreads[i] = t1;
}
Initial HP of Gareen: 100.000000
Thread-0 started
Thread-1 started
Thread-2 started
Thread-3 started
Thread-4 started
Thread-5 started
Thread-6 started
Thread-7 started
Thread-8 started
Thread-9 started
Thread-5: Gareen lost 1 HP,now his HP: 94.0
Thread-5 ended
Thread-3: Gareen lost 1 HP,now his HP: 96.0
Thread-8: Gareen lost 1 HP,now his HP: 91.0
Thread-9: Gareen lost 1 HP,now his HP: 90.0
Thread-9 ended
Thread-7: Gareen lost 1 HP,now his HP: 92.0
Thread-7 ended
Thread-0: Gareen lost 1 HP,now his HP: 99.0
Thread-0 ended
Thread-3 ended
Thread-8 ended
Thread-2: Gareen lost 1 HP,now his HP: 97.0
Thread-6: Gareen lost 1 HP,now his HP: 93.0
Thread-4: Gareen lost 1 HP,now his HP: 95.0
Thread-6 ended
Thread-1: Gareen lost 1 HP,now his HP: 98.0
Thread-0 joined
Thread-2 ended
Thread-1 ended
Thread-4 ended
Thread-1 joined
Thread-2 joined
Thread-3 joined
Thread-4 joined
Thread-5 joined
Thread-6 joined
Thread-7 joined
Thread-8 joined
Thread-9 joined
Now, Gareen's HP: 90.0
Process finished with exit code 0
现在我完全糊涂了,是什么导致了这些现象,我有可能实现我想象的场景吗?
解决方案
不能保证线程按设定的顺序执行。您只需为此使用标准同步代码。
如果您只想在主线程之外以线性方式降低健康代码,则使用 1 线程并执行其中的所有代码。例子:
Thread t1 = new Thread(() -> {
for (int i = 0; i < N; i++) {
gareen.hurt();
System.out.println(Thread.currentThread().getName() + ": Gareen lost 1 HP,now his HP: " + gareen.hp);
try{
Thread.sleep(3000);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " ended");
}
});
t1.start();
System.out.println(t1.getName() + " started");
这将达到减少后台线程运行状况的结果,以便您可以同时更新 UI、声音等
另一种解决方案是对 Hero 对象使用 Singleton - https://refactoring.guru/design-patterns/singleton和对象上的同步锁。然而,这不会产生与第一个解决方案显着不同的结果。
不明白为什么 join() 总是在被调用线程结束之后结束
Thread.join()
在您的场景中用于阻塞主线程,直到您调用的线程.join()
完成。
推荐阅读
- python - 为所有平台创建可执行文件
- django - 在打开 filefield(contains mp4 video) url 时,它拒绝访问它。但只有视频而不是其他类型,如图像
- r - 使用 dplyr 从每组的另一列中识别另一个日期之前的最近日期?
- makefile - CMake - 没有更改时不需要的重建
- linux - 在 Git 别名中使用条件?
- arrays - Powershell 在字段名称和值之间动态移动
- url - 使用 svelte-routing 小写的 URL
- java - .contains() 不适用于 BufferedReader
- ios - 如何在表格视图单元格中删除星级?
- google-sheets - 从另一个谷歌表中获取价值