Reputation: 465
I want to varify the Thread
mechanism, so I made a Company
class as following:
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:
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:
Company
class should end after each IWork objects starts to work but it seems doesn't.ThreadTest
and t1/t2/main thread could run separately.How could I modify my code of Company
to let it go with my expectation (question 1~3), and why?
Thanks a lot.
Upvotes: 1
Views: 155
Reputation: 14661
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. 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).
With this sleep
and yield
mean different things, even though the effect is sortof the same.
sleep
says "dear scheduler, this thread wants to do nothing for the next x ms"yield
says "dear scheduler now is an awesome time to let other threads do stuff".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
Same issue as above. All your threads are busy loops, hogging the CPU until they finish.
Finally it seems the boss never stops his working...
As per the comment below, endCount is not thread safe. You could wrap it in an AtomInteger, or add some synchronized blocks.
As an unrelated aside, you should think about the Boss not being a Thread at all. 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.
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.
Upvotes: 1
Reputation: 28289
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.
You can use AtomicInteger
,
AtomicInteger endCount
and
endCount.incrementAndGet()
instead endCount++
endCount.get() < members.size()
instead
endCount<members.size()
so the JMM can gurantee Boss
get the fresh value in its loop.
And, as suggested in the comment, you can add this in the loop of Worker
, it will be easier to simulate multi-thread
enviroment:
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. You can check this.
Even in your second test, besides 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.
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!
Upvotes: 1