Q:

Understanding wait() and notify() methods

I'm trying to understand how Java's wait and notify methods work. As per the documentation, wait() causes thread to wait for subsequent calls to notify() or notifyAll() methods but for some reason notify doesn't interrupt "waiting":
public static void main(String[] args) {

    Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("thread1 is started, waiting for notify()");

            synchronized (this) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    System.out.println(e.getLocalizedMessage());
                }
            }

            System.out.println("waiting is over");
        }
    });

    thread1.start();

    // unblock thread1 in 2 seconds

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

    synchronized (thread1) {
        thread1.notify();
    }
}

A:

You need to notify the object that is being waited on, not the thread that is waiting.
In your case the object waited on is an instance of an anonymous inner class, which is problematic because you cannot easily obtain a reference to it in order to notify it. You could solve this by extending Thread directly:
Thread thread1 = new Thread() {
    @Override
    public void run() {
        System.out.println("thread1 is started, waiting for notify()");

        synchronized (this) {
            try {
                wait();
            } catch (InterruptedException e) {
                System.out.println(e.getLocalizedMessage());
            }
        }

        System.out.println("waiting is over");
    }
};

Now the this (in synchronized (this)) refers to the thread itself, and the wait is called on the thread object too. In this case your current call to notify should be fine, since it notifies the same object (which happens in this case to be the thread that is waiting - but just to be clear, that need not be the case).
It isn't considered good practice to use an object for synchronisation that may also be used elsewhere; instances of Thread would be an example of this, and in fact the documentation specifically advises against it:

It is recommended that applications not use wait, notify, or notifyAll on Thread instances.

Also, you should correctly handle spurious wakeup; that is, wait may return because notify/notifyAll was called elsewhere or perhaps was not even called at all. As the documentation also says:

A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied. In other words, waits should always occur in loops [...]

Therefore, your example should really use a separate variable to track whether the wakeup was intentional (due to an explicit notify) or not.

