Same thread is able to execute on same objects two

2019-08-30 18:36发布

问题:

As I Know only one thread can execute on a synchronize method on same block but in below producer consumer problem I am able to run both the methods.

Sample Code

import java.util.concurrent.CountDownLatch;

public class VIV {

    public static void main(String[] args) throws Exception {
        Number no = new Number();

        //Same Object is passed
        Even ev = new Even(no, 10);
        Odd od = new Odd(no, 10);

        Thread oddThraed = new Thread(od,"ODD");
        oddThraed.start();

        Thread evenThraed = new Thread(ev,"Even");
        evenThraed.start();
    }
}

class Number {

    int no;
    boolean flag=false;

    public synchronized int getEvenNo() {
        System.out.println("In Even Method");
        // wait block so no other thread can enter on same object synchronized method
        try{
            wait();
        }catch (Exception e) {
            // TODO: handle exception
        }

        if(!flag) {
            try {
                Thread.sleep(1000);
            }catch (Exception e) {
                // TODO: handle exception
            }
        }   

        no=no+1;
        System.out.println(Thread.currentThread().getName()+":"+no);
        flag=false;
        notify();
        return no;
    }

    public synchronized int getOddNo() {
        System.out.println("In ODD Method");
        // wait block so no other thread can enter on same object synchronized method
        try{
            wait();
        }catch (Exception e) {
            // TODO: handle exception
        }

        if(flag) {
            try{
               wait();
            }catch (Exception e) {
                // TODO: handle exception
            }
        }   
        no = no+1;
        System.out.println(Thread.currentThread().getName()+":"+no);
        flag=true;
        notify();
        return no;
    }

}

class Even implements Runnable {
    Number num;
    int noOfTime;

    Even(Number no, int noOfTime) {
        this.num=no;
        this.noOfTime=noOfTime;
    }

    public void run() {
        for(int i=0;i<noOfTime;i++) {
            num.getEvenNo();
        }   
    }
}

class Odd  implements Runnable {

    Number num;
    int noOfTime;

    Odd(Number no, int noOfTime) {
        this.num=no;
        this.noOfTime=noOfTime;
    }   

    public void run() {
        for(int i=0;i<noOfTime;i++) {
            num.getOddNo();
        }   
    }
}

OutPut :

  1. In ODD Method
  2. In Even Method

As Only one Number object is created and passed to other classes which are invoking on its two different synchronized methods. Both methods are printing the message after that waiting .

回答1:

The reason that both methods get executed at the same time is that the wait() method releases the lock. Once your synchronized method calls wait(), the lock is given back, and another thread can invoke another synchronized method on the same object. Don't call wait() inside a synchronized method in this way!

The wait() calls are also the reason that you're getting deadlock. This is what is happening:

  1. The odd method acquires the lock and starts executing.
  2. The odd method prints its first message.
  3. The odd method invokes wait(), which releases the lock and waits to be notified.
  4. The even method can now acquire the lock that's been released.
  5. The even method prints its first message.
  6. The even method invokes wait(), which releases the lock and waits to be notified.

By this point, you're in the middle of both synchronized methods (because wait() releases the lock), and you're deadlocked (because both methods are waiting).

Don't call wait() unless you're quite sure that's what you need. If your wait() is there just to keep it waiting to see if the synchronization can be broken, you might try Thread.sleep() instead, which will pause without releasing any locks. Usually it's enough to declare the relevant methods or blocks as synchronized without needing any wait / notify.

(By the way, it's not a good idea to have a class called Number, because this is a standard JDK class. It's the superclass of Double, Integer, and so on.)



回答2:

I don't know whether you are trying to achieve this kind of thing, if you are trying to call EVEN & ODD alternatively, then I have modified your code for the same. Have a look at below code :-

public class VIV {

    public static void main(String[] args) throws Exception {

        TestNumber no = new TestNumber();
        // Same Object is passed
        Even ev = new Even(no, 10);
        Odd od = new Odd(no, 10);

        Thread oddThraed = new Thread(od, "ODD");
        oddThraed.start();

        Thread evenThraed = new Thread(ev, "Even");
        evenThraed.start();
    }
}

class TestNumber {

    int     no;
    boolean flag    = false;

    public synchronized int getEvenNo() {
        System.out.println("In Even Method");
        // wait block so no other thread can enter on same object synchronized
        // method

        no = no + 1;
        System.out.println(Thread.currentThread().getName() + ":" + no);
        flag = false;
        notify();

        try {
            wait();
        } catch (Exception e) {
            // TODO: handle exception
        }

        if (!flag) {
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        return no;
    }

    public synchronized int getOddNo() {
        System.out.println("In ODD Method");
        no = no + 1;
        System.out.println(Thread.currentThread().getName() + ":" + no);
        flag = true;
        notify();
        // wait block so no other thread can enter on same object synchronized
        // method
        try {
            wait();
        } catch (Exception e) {
            // TODO: handle exception
        }

        if (flag) {
            try {
                wait();
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        return no;
    }
}

class Even implements Runnable {
    TestNumber  num;
    int     noOfTime;

    Even(TestNumber no, int noOfTime) {
        this.num = no;
        this.noOfTime = noOfTime;
    }

    public void run() {
        for (int i = 0; i < noOfTime; i++) {
            num.getEvenNo();
        }
    }
}

class Odd implements Runnable {

    TestNumber  num;
    int     noOfTime;

    Odd(TestNumber no, int noOfTime) {
        this.num = no;
        this.noOfTime = noOfTime;
    }

    public void run() {
        for (int i = 0; i < noOfTime; i++) {
            num.getOddNo();
        }
    }
}

Note : As suggested by @chiastic-security Number is already defined in JDK, I have renamed it to TestNumber in this code.

Output :

In ODD Method
ODD:1
In Even Method
Even:2
In ODD Method
ODD:3
In Even Method
Even:4
In ODD Method
ODD:5
In Even Method
Even:6
In ODD Method
ODD:7
In Even Method
Even:8
In ODD Method
ODD:9
In Even Method
Even:10
In ODD Method
ODD:11
In Even Method
Even:12
In ODD Method
ODD:13
In Even Method
Even:14
In ODD Method
ODD:15
In Even Method
Even:16
In ODD Method
ODD:17
In Even Method
Even:18
In ODD Method
ODD:19
In Even Method
Even:20