Concurrent Programming with Java threads:
sine+cosine = ?
Two threads are moving the same ball; one thread
follows a sine path, and the other a cosine path. Color of the ball changes
each time it is painted, to visualize the ball moves. One expects a circle...
The ball
class Ball
{
double x, y, angleX = 0, angleY = 0;
int radius = 8, steps = 100, scale = 5;
Color color;
public Ball(int x,int y,Color color){
this.x = x; this.y = y; this.color = color;
}
public Ball(int x,int y){ this(x,y,ColorBall.nextColor()); }
public void paint(Graphics g){
g.setColor(color);
g.fillOval((int)x-radius,(int)y-radius,radius*2,radius*2);
}
public void sine(){
angleY += (2*Math.PI/steps);
y += scale*Math.sin(angleY);
}
public void cosine(){
angleX += (2*Math.PI/steps);
x += scale*Math.sin(angleX+Math.PI/2);
}
}
Thread for sine moving
class Sin extends Thread
{
Ball ball;
Applet applet;
public Sin(Ball b,Applet a){ ball = b; applet = a; }
public void run(){
while(true){
ball.sine();
applet.paint(applet.getGraphics());
}
}
}
The Applet
public class SinCos extends Applet
{
Ball ball = new Ball(startx,starty);
public void paint(Graphics g) {
super.paint(g);
ball.paint(g);
ball.color = ColorBall.nextColor();
}
void circle(Ball ball){
new Sin(ball,this).start();
new Cos(ball,this).start();
}
public void init(){ circle(ball); }
}
Interpretation: as the two threads do not cooperate in
any way, it is the scheduler task to switch from one to the other. Switches
occur at arbitrary moments that are visualized by lines and colums in the
output. In any case, one does not get a circle.
Use of Yield
class Sin extends Thread
{
Ball ball;
Applet applet;
public Sin(Ball b,Applet a){ ball = b; applet = a; }
public void run(){
while(true){
ball.sine();
applet.paint(applet.getGraphics());
Thread.yield();
}
}
}
Interpretation: now, the two threads cooperate by
returning control to the scheduler after each move. However, the scheduler
has complete freedom to choose the next thread to run; moreover, switches
can still occur at arbitrary moments. This solution would certainly works
with a cooperative scheduler; but, one looks for a portable solution. By
the way, the output shows that sine+cosine=square is a possible solution
with standard Java threads...
Use of Sleep
class Sin extends Thread
{
Ball ball;
Applet applet;
public Sin(Ball b,Applet a){ ball = b; applet = a; }
public void run(){
while(true){
ball.sine();
applet.paint(applet.getGraphics());
try{Thread.sleep(1);}catch(Exception e){System.out.println(e);}
}
}
}
Interpretation: Using Thread.sleep forces the scheduler
to choose the other thread as next thread to run, because the running thread
falls asleep for a while. But, it has the choice when the two threads are
awake, which occurs periodically (remember that there is a third thread,
behing the scene, for dealing with graphics). Thus, one gets something
wich is near a circle, but which is not a circle. Moreover, the output
depends on the value of the sleeping delay. One thus certainly cannot get
a portable solution in this way.
The solution
class Sin extends Thread
{
Ball ball;
Applet applet;
public Sin(Ball b,Applet a){ ball = b; applet = a; }
public void run(){
while(true){
synchronized(ball){
ball.sine();
applet.paint(applet.getGraphics());
ball.notify();
try{ball.wait();}catch(Exception e){System.out.println(e);}
}
}
}
}
Interpretation: After having awaken the other thread,
the currently running one falls asleep, waiting to be awaken later by a
notification on the ball. The two threads are thus executed in turn. One
gets a circle: sine+cosine=circle, which of course was the expected solution.
Note however that there are too much painting actions performed because
both threads are calling paint (this cannot be avoided as soon as a symetric
solution is searched).
What About Adding a New
Thread ?
public void init(){
circle(ball);
new Cos(ball,this).start();
}
Interpretation: Now, there are three threads and the
scheduler has no way to run them at the same pace. One does not get a Lissajous
figure, which is the expected output. The question is: how to get such
a figure? This would certainly need a more complex solution, with phases
during which the scheduler runs the three threads. Alternatively, a solution
consists in using Fair Threads; see what fair
threads are by clicking
here.
Click here
to get the demos source code