@Test
	public void testNotify() {
		final Object o = new Object();
		
		Runnable r = new Runnable(){

			@Override
			public void run() {
				
				assertThreadIsRunnable();
				
				try {
					synchronized(o){
						o.wait(); 
						// wait can only be invoked on an object if the thread which invoked wait owns the monitor lock on the object
						// this means that wait() inside a synchronized block and on an object which is the same as that used in the block
						System.out.println("Thread has been woken!");		
						
						while(true){
							int x = 0;
						}
					}
					
				} catch (Exception e) {
					e.printStackTrace();
					setShouldTerminateTest(true);
				}
			}
			
		};
		
		Thread t = new Thread(r);
		t.setName("thread1");
	    this.setThread(t);
		
		assertThreadIsNew();
		t.start();
		
		while(!t.getState().equals(Thread.State.WAITING) && !shouldTerminateTest){
			System.out.println("waiting for thread to enter WAITING state");
		}
		
		assertThreadIsWaiting();
		System.out.println("Thread entered WAITING state");
		
		synchronized(o){
			o.notify();
			// wait can only be invoked on an object if the thread which invoked wait owns the monitor lock on the object
			// this means that wait() inside a synchronized block and on an object which is the same as that used in the block
			System.out.println(th.getName()+": "+th.getState());
		}

		while(t.getState().equals(Thread.State.WAITING) || t.getState().equals(Thread.State.BLOCKED)){
			System.out.println("waiting for thread to enter RUNNABLE state");
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		assertThreadIsRunnable();
		System.out.println("Thread entered RUNNABLE state");
		
	}