Java中每一个对象都可以成为一个监视器(Monitor), 该Monitor由一个锁(lock), 一个等待队列(waiting queue), 一个入口队列(entry queue).
对于一个对象的方法, 如果没有synchronized关键字, 该方法可以被任意数量的线程,在任意时刻调用。
对于添加了synchronized关键字的方法,任意时刻只能被唯一的一个获得了对象实例锁的线程调用。
synchronized用于实现多线程的同步操作
synchronized的三种应用方式,Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:
wait(), notify(), notifyAll() 和 synchonized 需要搭配使用, 用于线程同步。
wait()总是在一个循环中被调用,挂起当前线程来等待一个条件的成立。 Wait调用会一直等到其他线程调用notifyAll()时才返回。
当一个线程在执行synchronized 的方法内部,调用了wait()后, 该线程会释放该对象的锁, 然后该线程会被添加到该对象的等待队列中(waiting queue), 只要该线程在等待队列中, 就会一直处于闲置状态, 不会被调度执行。 要注意wait()方法会强迫线程先进行释放锁操作,所以在调用wait()时, 该线程必须已经获得锁,否则会抛出异常。由于wait()在synchonized的方法内部被执行, 锁一定已经获得, 就不会抛出异常了。
wait(), notify(), notifyAll() 和 synchonized 需要搭配使用, 用于线程同步。
当一个线程调用一个对象的notify()方法时, 调度器会从所有处于该对象等待队列(waiting queue)的线程中取出任意一个线程, 将其添加到入口队列( entry queue) 中. 然后在入口队列中的多个线程就会竞争对象的锁, 得到锁的线程就可以继续执行。 如果等待队列中(waiting queue)没有线程, notify()方法不会产生任何作用。
notifyAll() 和notify()工作机制一样, 区别在于notifyAll()会将等待队列(waiting queue)中所有的线程都添加到入口队列中(entry queue)。
我们可以先来看看,不放在synchronized代码块中使用会有什么问题?
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
obj.wait();
obj.notify();
}
输出结果:
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at xxxx.main(XXX.java:254)
可以看到上面的代码输出结果,直接抛出异常了。下面就讲讲为何会出现这种问题。
wait() 和 notify()是用来实现多个线程之间的一个协调;wait() 表示让线程进入到阻塞状态,notify() 表示让阻塞的线程被唤醒, notifyAll() 则表述唤醒所有被阻塞的线程。 wait() 和 notify() 必然是成对出现的。 如果一个线程被wait()方法阻塞, 那么必然要另一个线程通过notify() 唤醒,从而实现多个线程之间的通信。
在多线程里面要实现多个线程之间的通信,除了管道以外,只能通过共享变量的方式来实现,也就是说第一个线程修改了公共共享变量,别的线程获得修改后的变量的值,从而完成数据的一个通讯;但是多线程的本身是具有并行执行的一个特性,也就是说,在同一个时间,多个线程是可以同时执行的,那么这种情况下,其它现在在访问共享变量之前,必须要知道,第一个线程已经修改了这个共享变量,否则就需要等待,或者是拿到原始数据,基于此基础上运输,产生数据不一致的场景;同时第一个线程在数据修改后,还需要把那个已经处于等待状态下的其它线程唤醒,所以在这种场景下,需要去实现线程之间的通信就必须要有一个静态条件,去控制多线程什么时候条件等待什么时候条件唤醒。而synchronized关键字就可以实现这样一个互斥条件,也就是在通过共享变量来实现多个线程通信的一个场景里面,参与通信的线程必须竞争到这共享变量的一个锁资源,才能够有资格对共享变量进行修改,那么修改完之后释放锁,其他线程就可以再次竞争同一个共享的锁,来获取修改之后的数据,从而完成线程之间的一个通信;所以这就是为什么wait() / notify() 必须需要在synchronized代码块中使用。
有了synchronized同步锁,就可以实现对于多个通信线程之间的一个互斥,从而实现条件等待和条件互斥的唤醒,另外为了避免wait() 和 notify()的错误使用,JDK强制的要求把wait() 和 notify() 写在了同步代码块中,否则就会抛出IllegalMonitorStateException。
/*
* synchronized (obj) {
* while (<condition does not hold>)
* obj.wait(timeout, nanos);
* ... // Perform action appropriate to condition
* }
*
* This method should only be called by a thread that is the owner
* of this object's monitor. See the {@code notify} method for a
* description of the ways in which a thread can become the owner of
* a monitor.
*
* @param timeout the maximum time to wait in milliseconds.
* @param nanos additional time, in nanoseconds range
* 0-999999.
* @throws IllegalArgumentException if the value of timeout is
* negative or the value of nanos is
* not in the range 0-999999.
* /