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); }
}

result

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();         
        }
    }
}

result

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);}
        }
    }
}
 

result

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);}       
          }   
       }
    }
}

  result

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(); 
}

result

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