Home > Net >  Why these threads no started and ended orderly?how to fix it? [closed]
Why these threads no started and ended orderly?how to fix it? [closed]

Time:09-27

I'm a newbie in Java multiply Thread and I stuck in my research for a exercise what I find from a tutorial:

Imagine this scene: I have an 100hp gareen, and it have been attacked by 10 times,and my code is:

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);
    
    }
}



In my imagine, it should be like(I spaced the result () for readability):

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

But the reality is(same, spaced for readability):

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

I don't understand why the join() always end after which called thread ends but know that it blocked the last statement System.out.println("Now, Gareen's HP: " gareen.hp);

to make sure it will be printed at the end.

According printed result, seems like though every threads start in almost same time, but their do things and ends are all different. Another weird part is I find that the changed HP is correctly cope with the thread tag like t0 - 99,t2 - 98, ...t9 - 90, but their don't print in descend orderly, I spent very long time to realized a possible reason: What message the system prints don't match what actually the threads doing,

I even find that the two print statements in defined Thread don't run orderly when I delete the Thread.sleep() , seems like the code in Threads actually don't run in order. I'm always think that Thread just a function that don't run orderly with synchronize code outside.

 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


Now I'm completely confused, what cause these phenomenon, and can I possible to realize the scene I imagined ?

CodePudding user response:

There is no guarantee that threads execute in a set order. You would just use standard synchronous code for that.

If you just want to make the health code down in a linear fashion outside the main thread, then use 1 Thread and execute all code within that. Example:

        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");

This would achieve the result of reducing the health on a background thread so that you can simultaneously update the UI, sounds, etc


Another solution would be to use a Singleton for the Hero object - https://refactoring.guru/design-patterns/singleton and a synchronized lock on the object. However this would not produce a significantly different result from the first solution.


don't understand why the join() always end after which called thread ends

Thread.join() in your scenario is used to block the main thread until the thread you called .join() on completes.

  • Related