繁体   English   中英

Java线程无法正常运行

[英]Java Thread not go correctly

我想改变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");
    }
}

运行该应用程序时,出现了意外的结果:

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

但是在另一种实践中, 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!");
    }
}

我运行它并得到:

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

这些使我感到困惑:

  1. 我希望Company类的主线程应该在每个IWork对象开始工作后结束,但似乎没有。
  2. 我希望汤姆和玛丽可以一起工作,但结果是玛丽在汤姆工作结束后开始工作。
  3. 老板似乎永不停止工作。
  4. [更新/附加此问题:]我不必为ThreadTest添加Thread.yield()或Thread.sleep(),t1 / t2 /主线程可以单独运行。

我如何修改我的Company代码以使其符合我的期望(问题1〜3),为什么?

非常感谢。

唯一的问题是endCount上没有同步,因此当TomMary调用Boss onEnd方法并递增endCount ,工作的Boss可能不会注意到它。

您可以使用AtomicInteger

AtomicInteger endCount

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

这样JMM可以保证Boss在其循环中获得新的价值。


并且,正如注释中所建议的,您可以在Worker循环中添加此代码,这将更容易模拟multi-thread环境:

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

}

更新

启动多个线程时,如果不同步,则无法控制它们中每一行的执行顺序。 它们的执行顺序由CPU安排。 您可以检查一下

即使在第二次测试中,除了Main: Start! 确保在第一行显示,其他行的顺序仍然不确定

而且, Thread.sleepThread.yield只会使仿真并发执行变得更加容易,但仍不保证TomMary会在控制台上逐行输出内容。


这是我计算机上的测试结果:

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!

我希望汤姆和玛丽可以一起工作,但结果是玛丽在汤姆工作结束后开始工作

因为您没有让线程产生任何逻辑位置(例如IO),所以不能保证上下文会切换。 调度程序不会在此处强制执行此操作,因此它们都可以运行直到串行完成为止。

您可以通过添加Thread.yield() (或进行睡眠或执行一些IO)来强制执行此操作。

即使效果差不多, sleepyield意味着不同的事情。

  • sleep说:“亲爱的调度程序,此线程不希望在接下来的x毫秒内执行任何操作”
  • yield说:“亲爱的调度程序现在是让其他线程做事情的好时机”。

选择最接近您想要说的一种。

我希望主线程应该在每个IWork对象开始工作后结束,但似乎没有

与上述相同。 您所有的线程都是繁忙的循环,占用CPU直到完成。

终于,老板似乎永不停止工作。

根据下面的评论,endCount不是线程安全的。 您可以将其包装在AtomInteger中,或添加一些同步块。

顺带一提,您应该考虑Boss根本不是线程。 她可以仅通过回调来实现。

[更新/附加此问题:]我不必为ThreadTest添加Thread.yield()或Thread.sleep(),t1 / t2 /主线程可以单独运行。

调度程序按照调度程序的要求进行操作。 如果您第一次运行多次,则不能保证在任何一个应用程序中都能获得相同的结果。

暂无
暂无

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

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