Olgidos
Olgidos

Reputation: 231

Java thread wont synchronize

I just started working with notify, synchronized and wait, and it almost works, but only when I let the second Thread sleep() for 1 ms. You can see my console output at the end.

My main:

public static void main(String[] args) {

    InfoPaket paket = new InfoPaket();
    TestThread testThread = new TestThread(paket);
    TestThread2 testThread2 = new TestThread2(paket);
}

My "Lock" Class

public class InfoPaket {

String info;
char infoDataSign;
boolean newInfo = false;



public synchronized boolean isNew(){
    if (!newInfo){
        try {

            System.out.println("waiting");
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    return newInfo;
}


public synchronized String getInfo() {
    newInfo = false;
    return info;
}
public synchronized void setInfo(String info) {this.info = info;
    newInfo = true;
    notify();
}

My two test Thread

1.

public class TestThread implements Runnable {

InfoPaket info;
int i = 0;

public TestThread(InfoPaket info)  {
    this.info = info;
    new Thread(this,"TestThread").start();
}   



public void run() {
    while(true){
        i++;
        getInfo();
    }
}



 void getInfo(){
     info.isNew();
     System.out.println("he got it... " + info.getInfo() + "  " + i);
    }

2.

public class TestThread2 implements Runnable{


InfoPaket info;
Thread t;
int i = 0;

public TestThread2(InfoPaket info)  {
    this.info = info;
    t = new Thread(this,"TestThread");
    t.start();
}   



public void run() {
    while(i < 500000){
        i++;
        setInfo();
    }
}



 void setInfo(){

     info.setInfo("lelaoao");
     System.out.println("here  " + i);

     try {
        t.sleep(1);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    }   

Here my results which are very clear and nice (except for the start):

waiting
he got it... lelaoao  1
waiting
here  1
here  2
he got it... lelaoao  2
waiting
here  3
he got it... lelaoao  3
waiting
here  4
he got it... lelaoao  4
waiting
here  5
he got it... lelaoao  5
waiting
here  6
he got it... lelaoao  6

And so on..

But putting the 2. thread everytime asleep slows down the program, but without it I get something strange that I can't explain to myself:

waiting
here  1
he got it... lelaoao  1
he got it... lelaoao  2
waiting
here  2
here  3
he got it... lelaoao  3
he got it... lelaoao  4
waiting
here  4
here  5
here  6
here  7
here  8
here  9
here  10
he got it... lelaoao  5
he got it... lelaoao  6
waiting

Upvotes: 1

Views: 92

Answers (1)

hagrawal7777
hagrawal7777

Reputation: 14658

You code is working as expected (as coded), except - 1) You have some bad code 2) You may have misunderstood the concept.

Let me first start by saying what your code is doing:

  1. You have a InfoPaket which is shared among your 2 thread and hold info about packet, and keeps track whether new info is received or not.
  2. Then your have TestThread which will check if new info is received or not, if new info is not recieved then it will wait and once new info is received then you will print the info (which is always "lelaoao") along with your loop counter, like this he got it... lelaoao 23
  3. Then your have TestThread2 which will set the info and notify the waiting thread and then print the loop counter of this thread like this - "here " + i.

Now, most important thing for your understand is that thread scheduling is unexpected and depends on underlying OS thread scheduling mechanism as well as JVM implementation, so you cannot expect that if thread 2 has set info then then certainly thread 1 will execute, you can try to enforce it putting Thread.sleep(1) or Thread.yeild(), please note that Thread.yeild() is not portable and it is good that you are not using it and should not be use it, instead of it you should use Thread.sleep(1)

Now let come to bad code and some important concepts:

Bad code

  1. The most wrong thing you were doing is you were starting a new thread from the constructor, don't ever try to start a new thread from the constructor because it will cause your reference to leak even before your constructor has completed. Read this excellent article.
  2. You don't need a reference of current thread in TestThread2 because you can directly do Thread.sleep(1); which will cause current thread to sleep.
  3. You were printing System.out.println("he got it... " + info + " " + i); and System.out.println("here " + i); from your main thread, however you should print these from synchronized block to ensure that there is no interleaving, because in absence of synchronization interleaving could occur and you could see he got it... lelaoao 3 before here 3 which is logically wrong.

Now, below is the fixed code which will consistent produce correct result (considering your are giving thread 1 chance to run once thread 2 has set info), output is also placed in the end.

InfoPaket.java

public class InfoPaket {

    String info;
    char infoDataSign;
    boolean newInfo = false;

    public synchronized boolean isNew() {
        if (!newInfo) {
            try {
                System.out.println("waiting");
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return newInfo;
    }

    public synchronized void getInfo(int i) {
        newInfo = false;
        System.out.println("he got it... " + info + "  " + i);
    }

    public synchronized void setInfo(String info, int i) {
        this.info = info;
        newInfo = true;
        System.out.println("here  " + i);
        notify();
    }

    public static void main(String[] args) {

        InfoPaket paket = new InfoPaket();
        TestThread testThread = new TestThread(paket);
        TestThread2 testThread2 = new TestThread2(paket);

        new Thread(testThread, "testThread").start();
        new Thread(testThread2, "testThread2").start();
    }
}

TestThread.java

public class TestThread implements Runnable {

    InfoPaket info;
    int i = 0;

    public TestThread(InfoPaket info) {
        this.info = info;
    }

    public void run() {
        while (true) {
            i++;
            getInfo(i);
        }
    }

    void getInfo(int i2){
         info.isNew();
         info.getInfo(i2);
    }
}

TestThread2.java

public class TestThread2 implements Runnable {

    InfoPaket info;
    int i = 0;

    public TestThread2(InfoPaket info) {
        this.info = info;
    }

    public void run() {
        while (i < 500000) {
            i++;
            setInfo(i);
        }
    }

    void setInfo(int i2) {

        info.setInfo("lelaoao", i2);

        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
} 

Output:

waiting
here  1
he got it... lelaoao  1
waiting
here  2
he got it... lelaoao  2
waiting
here  3
he got it... lelaoao  3
waiting
here  4
he got it... lelaoao  4
waiting
here  5
he got it... lelaoao  5
waiting
here  6
he got it... lelaoao  6
waiting
here  7
he got it... lelaoao  7
waiting
here  8
he got it... lelaoao  8
waiting
here  9
he got it... lelaoao  9
waiting
here  10
he got it... lelaoao  10
waiting
here  11
he got it... lelaoao  11
waiting
here  12
he got it... lelaoao  12
waiting
here  13
he got it... lelaoao  13
waiting
here  14
he got it... lelaoao  14
waiting
here  15
he got it... lelaoao  15
waiting
here  16
he got it... lelaoao  16
waiting
here  17
he got it... lelaoao  17
waiting
here  18
he got it... lelaoao  18
waiting
here  19
he got it... lelaoao  19
waiting
here  20
he got it... lelaoao  20
waiting
here  21
he got it... lelaoao  21
waiting
here  22
he got it... lelaoao  22
waiting
here  23
he got it... lelaoao  23
waiting
here  24
he got it... lelaoao  24
waiting
here  25
he got it... lelaoao  25
waiting
here  26
he got it... lelaoao  26
waiting
here  27
he got it... lelaoao  27
waiting
here  28
he got it... lelaoao  28
waiting
here  29
he got it... lelaoao  29
waiting
here  30
he got it... lelaoao  30
waiting
here  31
he got it... lelaoao  31

Important concepts to be clear about

I think you might be missing some concept so you are thinking output to be crazy.

  1. Once you have set the info using thread 2 there is no guarantee that thread 1 will run because thread scheduling is unexpected and depends on underlying OS thread scheduling mechanism as well as JVM implementation, I have highlighted more point about this in the start with Thread.yield() and Thread.sleep(). With that note, if you don't use Thread.sleep(1) then you just can't expect output to be consistent, as I have shown below.
  2. When you do Thread.sleep then it doesn't release the lock, if that thread is acquiring some lock.
  3. Once you do notify(); then you can't expect the waiting thread to be "runnable" immediately and the thread which is acquiring the lock will not release immediately release the lock as soon as notify(); is called, lock will be released only once that synchronized block/method is finished.
  4. If you don't do Thread.sleep(1) in your 2nd thread then you can't expect the consistent output, and reasons I have explained above.

OP's comment:

Do you know any a more efficent way of transmitting data from on thread to an other one?

Either you have "shared memory" or "message passing"; shared memory is what we are doing in this case, and if you want to go for message passing then you can go for Java API like blocking queue (https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html) implementation, but that's becomes all together different story.

Upvotes: 1

Related Questions