简体   繁体   English

Java线程无法正常运行

[英]Java Thread not go correctly

I want to varify the Thread mechanism, so I made a Company class as following: 我想改变Thread机制,所以我做了一个Company类,如下所示:

public class Company {
    static void p(String s){ System.out.println(s); }

    interface IWork{ void work(); }
    interface OnReportListener{ int onEnd(Worker w); }
    static class Job{ int effertCount, budget=100; }

    static class Worker implements IWork{
        String name; Job job=new Job(); OnReportListener listener; boolean isOver;
        public Worker(String n, OnReportListener l) {
            name = n; listener = l;
        }

        public void work() {
            new Thread(){
                public void run() {
                    while (!isOver) {
                        int spent = (int) Math.round(Math.random()*7-2) ;
                        if (spent<0) p(name+": I earned $"+(-spent));
                        isOver = (job.budget-=spent) <=0;
                        job.effertCount++;
                    }
                    p(name+": OMG, I got the salary $"+ listener.onEnd(Worker.this));
                }
            }.start();
        }
    }

    static class Boss implements IWork, OnReportListener{
        Set<Worker> members; int endCount;
        public Boss(Set<Worker> s){ members = s;}
        public int onEnd(Worker w) {
            p("Boss: "+w.name+", thanks for your effort, you deserve it!");
            endCount++;
            return w.job.effertCount*10;
        }

        public void work() {
            new Thread(){
                public void run() {
                    while (endCount<members.size()) { /*fool around*/ }
                    p("Boss: It's time to go home!");
                }
            }.start();
        }
    }

    public static void main(String[] args) {
        Set<Worker> workers = new HashSet<Worker>();
        Boss boss = new Boss(workers);
        Worker tom = new Worker("Tom", boss); 
        workers.add(tom); // hire Tom
        Worker mary = new Worker("Mary", boss); 
        workers.add(mary); // hire Mary

        p("Company.main: Start to work!");
        boss.work();
        tom.work();
        mary.work();
        p("Company.main: End of the assigning");
    }
}

When I ran the application, I got the unexpected results: 运行该应用程序时,出现了意外的结果:

Company.main: Start to work!
Tom: I earned $1
Tom: I earned $1
Tom: I earned $1
Tom: I earned $1
Tom: I earned $1
Tom: I earned $1
Tom: I earned $2
Tom: I earned $2
Tom: I earned $1
Tom: I earned $1
Tom: I earned $1
Tom: I earned $1
Tom: I earned $1
Tom: I earned $2
Tom: I earned $2
Tom: I earned $1
Tom: I earned $1
Tom: I earned $1
Tom: I earned $2
Tom: I earned $1
Boss: Tom, thanks for your effort, you deserve it!
Tom: OMG, I got the salary $770
Mary: I earned $1
Mary: I earned $2
Mary: I earned $2
Mary: I earned $1
Mary: I earned $1
Mary: I earned $1
Mary: I earned $2
Mary: I earned $1
Mary: I earned $2
Mary: I earned $1
Mary: I earned $1
Mary: I earned $2
Boss: Mary, thanks for your effort, you deserve it!
Mary: OMG, I got the salary $510
Company.main: End of the assigning

But in another practice, a ThreadTest class: 但是在另一种实践中, ThreadTest类:

public class ThreadTest extends Thread{
    static void p(String s){ System.out.println(s); }
    public ThreadTest(String s){ super(s); }
    public void run() {
        for (int i = 0; i < 25; i++) p(getName()+": "+i);
    }

    public static void main(String[] args) {
        p("Main: Start!");
        new ThreadTest("t1").start();
        new ThreadTest("t2").start();
        p("Main: Finish!");
    }
}

I ran it and got: 我运行它并得到:

Main: Start!
t1: 0
t1: 1
t1: 2
Main: Finish!
t2: 0
t2: 1
t2: 2
t2: 3
t2: 4
t2: 5
t1: 3
t1: 4
t1: 5
t1: 6
t2: 6
t2: 7
t2: 8
t2: 9
t2: 10
t2: 11
t2: 12
t2: 13
t2: 14
t2: 15
t2: 16
t2: 17
t2: 18
t2: 19
t2: 20
t2: 21
t2: 22
t2: 23
t2: 24
t1: 7
t1: 8
t1: 9
t1: 10
t1: 11
t1: 12
t1: 13
t1: 14
t1: 15
t1: 16
t1: 17
t1: 18
t1: 19
t1: 20
t1: 21
t1: 22
t1: 23
t1: 24

These confuses me: 这些使我感到困惑:

  1. I expect the main thread of Company class should end after each IWork objects starts to work but it seems doesn't. 我希望Company类的主线程应该在每个IWork对象开始工作后结束,但似乎没有。
  2. I expect Tom and Mary should work together but the result is Mary works after the end of Tom's working. 我希望汤姆和玛丽可以一起工作,但结果是玛丽在汤姆工作结束后开始工作。
  3. It seems the boss never stops his working... 老板似乎永不停止工作。
  4. [Update/Append this question:] I don't have to add Thread.yield() or Thread.sleep() for ThreadTest and t1/t2/main thread could run separately. [更新/附加此问题:]我不必为ThreadTest添加Thread.yield()或Thread.sleep(),t1 / t2 /主线程可以单独运行。

How could I modify my code of Company to let it go with my expectation (question 1~3), and why? 我如何修改我的Company代码以使其符合我的期望(问题1〜3),为什么?

Thanks a lot. 非常感谢。

The only problem is there is no synchronization on endCount , so when Tom and Mary call onEnd method of Boss and increment endCount , the working Boss may not notice it. 唯一的问题是endCount上没有同步,因此当TomMary调用Boss onEnd方法并递增endCount ,工作的Boss可能不会注意到它。

You can use AtomicInteger , 您可以使用AtomicInteger

AtomicInteger endCount

and

  • use endCount.incrementAndGet() instead endCount++ 使用endCount.incrementAndGet()代替endCount++
  • use endCount.get() < members.size() instead endCount<members.size() 使用endCount.get() < members.size()代替endCount<members.size()

so the JMM can gurantee Boss get the fresh value in its loop. 这样JMM可以保证Boss在其循环中获得新的价值。


And, as suggested in the comment, you can add this in the loop of Worker , it will be easier to simulate multi-thread enviroment: 并且,正如注释中所建议的,您可以在Worker循环中添加此代码,这将更容易模拟multi-thread环境:

try {
    Thread.sleep(10);
} catch (Exception e) {

}

Update 更新

When you start multiple threads, you can not controll the execution orders of each line of them without synchronization. 启动多个线程时,如果不同步,则无法控制它们中每一行的执行顺序。 Their execution order is scheduled by CPU. 它们的执行顺序由CPU安排。 You can check this . 您可以检查一下

Even in your second test, besides Main: Start! 即使在第二次测试中,除了Main: Start! is guranteed to be displayed at first line, other lines' order is still uncertain . 确保在第一行显示,其他行的顺序仍然不确定

And, Thread.sleep or Thread.yield will just make it easier to simulate concurrency execution, it is still not guranteed Tom and Mary will output something on the console line by line. 而且, Thread.sleepThread.yield只会使仿真并发执行变得更加容易,但仍不保证TomMary会在控制台上逐行输出内容。


Here is the test result on my computer: 这是我计算机上的测试结果:

Company.main: Start to work!
Company.main: End of the assigning
Tom: I earned $2
Mary: I earned $1
Mary: I earned $1
Mary: I earned $1
Tom: I earned $1
Tom: I earned $2
Mary: I earned $1
Tom: I earned $1
Tom: I earned $2
Mary: I earned $1
Mary: I earned $2
Tom: I earned $1
Mary: I earned $2
Mary: I earned $1
Mary: I earned $1
Mary: I earned $2
Mary: I earned $2
Mary: I earned $2
Tom: I earned $1
Tom: I earned $1
Mary: I earned $1
Tom: I earned $1
Mary: I earned $1
Tom: I earned $1
Mary: I earned $2
Tom: I earned $1
Mary: I earned $1
Tom: I earned $1
Mary: I earned $2
Mary: I earned $1
Tom: I earned $1
Boss: Tom, thanks for your effort, you deserve it!
Tom: OMG, I got the salary $590
Mary: I earned $1
Boss: Mary, thanks for your effort, you deserve it!
Mary: OMG, I got the salary $770
Boss: It's time to go home!

I expect Tom and Mary should work together but the result is Mary works after the end of Tom's working 我希望汤姆和玛丽可以一起工作,但结果是玛丽在汤姆工作结束后开始工作

Because you don't have any logical place for the thread to yield (for example IO), there is no guarantee that the context will ever switch. 因为您没有让线程产生任何逻辑位置(例如IO),所以不能保证上下文会切换。 The scheduler does not force the matter here, so they both run until completion in serial. 调度程序不会在此处强制执行此操作,因此它们都可以运行直到串行完成为止。

You can force the matter by adding a Thread.yield() (or sleep, or by doing some IO). 您可以通过添加Thread.yield() (或进行睡眠或执行一些IO)来强制执行此操作。

With this sleep and yield mean different things, even though the effect is sortof the same. 即使效果差不多, sleepyield意味着不同的事情。

  • sleep says "dear scheduler, this thread wants to do nothing for the next x ms" sleep说:“亲爱的调度程序,此线程不希望在接下来的x毫秒内执行任何操作”
  • yield says "dear scheduler now is an awesome time to let other threads do stuff". yield说:“亲爱的调度程序现在是让其他线程做事情的好时机”。

Select the one that is closest to what you want to say. 选择最接近您想要说的一种。

I expect the main thread should end after each IWork objects starts to work but it seems doesn't 我希望主线程应该在每个IWork对象开始工作后结束,但似乎没有

Same issue as above. 与上述相同。 All your threads are busy loops, hogging the CPU until they finish. 您所有的线程都是繁忙的循环,占用CPU直到完成。

Finally it seems the boss never stops his working... 终于,老板似乎永不停止工作。

As per the comment below, endCount is not thread safe. 根据下面的评论,endCount不是线程安全的。 You could wrap it in an AtomInteger, or add some synchronized blocks. 您可以将其包装在AtomInteger中,或添加一些同步块。

As an unrelated aside, you should think about the Boss not being a Thread at all. 顺带一提,您应该考虑Boss根本不是线程。 She could be implemented just via callbacks. 她可以仅通过回调来实现。

[Update/Append this question:] I don't have to add Thread.yield() or Thread.sleep() for ThreadTest and t1/t2/main thread could run separately. [更新/附加此问题:]我不必为ThreadTest添加Thread.yield()或Thread.sleep(),t1 / t2 /主线程可以单独运行。

The Scheduler does as the Scheduler wants. 调度程序按照调度程序的要求进行操作。 If you run the first multiple times you are not guaranteed to get the same results in either application. 如果您第一次运行多次,则不能保证在任何一个应用程序中都能获得相同的结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM