0

I tried Collision Simulation of a point and a line. The point move by adding 1 to point.x and point.y infinitely. At once, The main thread checks collision two objects. If the point collides to the line, it stops. It works very well, but happens some error. If main thread loops without delay, the result of checking method always returned 'False'(In case that should return 'True' absolutely). There was no error in the following some cases.

  1. Add small delay in main thread. (For example, add some code likes "System.out.println() or "Thread.sleep(1)")
  2. Change the collision checking method to 'synchronized'.

Why does this happen?

[Main Thread]

public static void main(String[] args) throws IOException {
    DrawingFrame d = new DrawingFrame();

    PointObject p = new PointObject(200,200);
    LineObject l = new LineObject(100,10,new Vector(500,Math.toRadians(60.0f)));

    p.move();

    d.addDrawable(p);
    d.addDrawable(l);

    while(true) {
        // if main thread loops infinitely without delay, 
        // the result of checking method always returned 'False'.
        // System.out.println();   <-- DELAY
        if(l.isAbove(p)) p.stop=true;
    }
}

[Move]

public void move() {
    new Thread(new Runnable() {

        @Override
        public void run() {
            while(!stop) {
                x++;
                y++;
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }).start();
}

[Check]

@Override
public boolean isAbove(PointObject p) {
    return Math.abs(p.y-(Math.tan(this.vector.radian)*(p.x-this.x)+this.y))<=ERROR;
}
jannis
  • 4,843
  • 1
  • 23
  • 53
CJH
  • 3
  • 1
  • what is the `stop` in your method? which modifiers it has? – Dred May 24 '19 at 09:18
  • All movable objects(PointObject, LineObject) has 'stop' value(default 'False'). public boolean stop=false; – CJH May 24 '19 at 09:27
  • Try to add some "log" in you `isAbove` method. To see, what points your x and y reached. It helps you to understand which moment throws incorrect – Dred May 24 '19 at 09:28
  • Thank you! But, If I add some "log" code for check, then Main Thread will has delay, it works very well. Therefore, I couldn't see this error with "Log". So if I run "Debug Mode", the value 'p.x' and 'p.y' is read correctly. – CJH May 24 '19 at 09:38

2 Answers2

0

So, the answer is You don't give time to your new Thread to iterate. I simplify your method like

public void main() {
     PointObject p = new PointObject(1, 1);
     LineObject l = new LineObject(5, 5);

    p.move();

    // d.addDrawable(p);
    //  d.addDrawable(l);

    while (true) {
         System.out.println("Thread - "+Thread.currentThread().getName() + ": in while  p.x =" + p.x + " p.y=" + p.y);
        if (l.isAbove(p)) {
            p.stop = true;
            break;
        }
    }
}


public class PointObject {

int x;
int y;
boolean stop = false;


public PointObject(int x, int y) {
    this.x = x;
    this.y = y;
}


public void move() {
    Thread thread = new Thread(
            new Runnable() {
                @Override
                public void run() {
                    while (!stop) {
                        x++;
                        y++;
                        try {
                            System.out.println("Thread - "+Thread.currentThread().getName() + ": in move x =" + x + " y=" + y);
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
    thread.setName("MY-THREAD");
    thread.start();
   }
}


public class LineObject {

int x;
int y;
int iteration;

public LineObject(int x, int y) {
    this.x = x;
    this.y = y;
    this.vector = vector;
}

public boolean isAbove(PointObject p) {
    System.out.println("Thread - "+Thread.currentThread().getName() + ": in isAbove  x = " + x + " y= " + y);
    int result = p.y + p.x - (this.x + this.y);
    System.out.println("result = " + result);
    System.out.println("iteration = " + iteration++);
    return result > 0;
    }
}

So, you can see that till 102nd iteration

Thread - main: in while  p.x =1 p.y=1
Thread - main: in isAbove  x = 5 y= 5
Thread - MY-THREAD: in move x =2 y=2
result = -6
iteration = 1

And only here (the 103rd iter) we can see

Thread - main: in while  p.x =3 p.y=3
Thread - main: in isAbove  x = 5 y= 5
result = -4
iteration = 103
Thread - main: in while  p.x =3 p.y=3
Thread - main: in isAbove  x = 5 y= 5

Actually, I can't understand, how you escape from your while(true) loop and how you get stop=true. As you see, I got some changing only in the 60th iteration. So, as it says everywhere you should create synchronized/sleep/pool/queue for multithreading.

Without volatile in boolean which said in comments I had 3000+ iterations, but with it I had 1700+. Anyway, we found your problem.

First of all, You don't escape from your while(true) and you should use add some pause to while(true) to give other Thread make its business

Dred
  • 1,076
  • 8
  • 24
  • `boolean stop` should be `volatile` (or it should be an `AtomicBoolean` instead). Otherwise the thread spawned in `move` might cache its value and never actually notice that it changed. https://stackoverflow.com/questions/106591/what-is-the-volatile-keyword-useful-for – jannis May 24 '19 at 11:38
  • @jannis, even that wouldn't help enough. I changed a little my answer with new statistic – Dred May 24 '19 at 12:12
  • I'm not talking about timing, this is a completely different issue with the code. – jannis May 24 '19 at 12:31
  • @jannis, Hmm, maybe I didn't understand you. But I've tried with `volatile` and that didn't help – Dred May 24 '19 at 12:34
  • @Dred My code works very well if post any code causing delay in main thread anywhere. Stopping movement works well likewise. If add "System.out.println()"(whatever contents), there is no problem in my code. Is there any other way? – CJH May 24 '19 at 14:09
  • @jannis Then, because coordinates of the point is not **volatile**, the main thread cannot read the value that "Point movement" thread write? – CJH May 24 '19 at 14:13
  • @CJH, you can try to make `isAbove` with `synchronized` modificator – Dred May 24 '19 at 14:23
  • @jannis How nice of you! When I add 'volatile' keyword to coordinate value, It works very well without delay codes. I understand that completely. Thank you for your help! – CJH May 24 '19 at 14:29
  • @Dred I solved this problem. Thank you for your answer! – CJH May 24 '19 at 14:32
0

Your problem is most probably caused by the fact that Point.x, Point.y and Point.stop are not volatile yet read by multiple threads. The thread may cache non-volatile members and never see the changes made by another thread. In simple words if you want to make sure that a change made to a field done by one thread is seen from another thread you need to make the field volatile. Refer to this question: What is the volatile keyword useful for.

Another tip (not related to the issue you're experiencing): You lack synchronization around Point.x and Point.y change - this may lead to inconsistent Point.x and Point.y reads (by calling isAbove after x++;, but before y++;), which I assume is not something you want. Simple locking example:

in move():

synchronize(this) {
    x++; y++;
}

in main():

synchronize(p) {
    if(l.isAbove(p)) p.stop = true;
}
jannis
  • 4,843
  • 1
  • 23
  • 53