我想要改变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
这些让我困惑:
Company
类的主线程应该结束,但似乎没有。ThreadTest
添加Thread.yield()或Thread.sleep(),并且t1 / t2 /主线程可以单独运行。我如何修改Company
的代码,让它符合我的期望(问题1~3),为什么?
非常感谢。
答案 0 :(得分:1)
唯一的问题是id,val
abc,{1|2|2|1}
上没有同步,因此当endCount
和Tom
调用Mary
onEnd
方法并增加Boss
时},工作endCount
可能不会注意到它。
您可以使用Boss
,
AtomicInteger
和
AtomicInteger endCount
代替endCount.incrementAndGet()
endCount++
代替
endCount.get() < members.size()
所以JMM可以保证endCount<members.size()
在其循环中获得新值。
并且,正如评论中所建议的那样,您可以在Boss
的循环中添加此内容,以便更轻松地模拟Worker
环境:
multi-thread
<强>更新强>
当你启动多个线程时,你无法在没有同步的情况下控制它们每一行的执行顺序。他们的执行顺序由CPU安排。您可以查看this。
即使在第二次测试中,除try {
Thread.sleep(10);
} catch (Exception e) {
}
保证在第一行显示外,其他行的顺序仍然不确定。
而且,Main: Start!
或Thread.sleep
只会使模拟并发执行变得更容易,但仍然没有保证Thread.yield
和Tom
将在控制台行输出一些内容线。
以下是我计算机上的测试结果:
Mary
答案 1 :(得分:1)
我希望汤姆和玛丽能够一起工作,但结果是玛丽在汤姆的工作结束后工作
因为你没有任何逻辑位置让线程产生(例如IO),所以不能保证上下文会切换。调度程序不会强制执行此操作,因此它们都会以串行方式运行直到完成。
您可以通过添加Thread.yield()
(或睡眠,或通过执行某些IO)来强制执行此操作。
这个sleep
和yield
意味着不同的东西,即使效果是相同的。
sleep
说&#34;亲爱的调度程序,这个帖子不想为下一个x ms做任何事情&#34; yield
说&#34;亲爱的调度程序现在是让其他线程做事的绝佳时机&#34;。选择最接近您想说的内容。
我希望主线程应该在每个IWork对象开始工作后结束,但似乎并没有
与上述相同的问题。所有线程都是繁忙的循环,占用CPU直到完成。
最后,老板似乎永远不会停止工作......
根据下面的评论,endCount不是线程安全的。您可以将其包装在AtomInteger中,或添加一些同步块。
作为一个无关的,你应该考虑Boss根本不是一个线程。她可以通过回调来实现。
[更新/附加此问题:]我不必为ThreadTest添加Thread.yield()或Thread.sleep(),并且t1 / t2 /主线程可以单独运行。
调度程序按调度程序的要求执行。如果您多次运行第一次,则无法保证在任一应用程序中获得相同的结果。