JAVA线程的synchronized、wait、notify、notifyall如何配合工做
概念:锁池、等待池、同步、资源锁、等待、唤醒
流程描述:
1.资源锁:多线程场景下的公共资源,资源锁对象有锁池和等待池两个区域
3.锁池,存放等待资源锁的线程
4.等待池,存放wait状态的线程,等待池中的线程不参与锁竞争。
5.线程获得资源锁后开始工做,能够经过wait方法临时释放锁进入等待池
6.等待池中的线程只有被唤醒才能从新竞争锁,若是竞争到锁,接着以前的点继续干活html
讲故事:
1.病人到医院交了挂号费,进入候诊区排队等着医生看病。这里医生就是公共资源,候诊区就是医生的锁池。
2.病人进入诊室让医生看病。这里就是从锁池中竞争到了锁,线程能够开始干活。
3.医生看了看病人的状况,告诉病人你这病只能我师父给看,我先作个登记,你回家等着吧,等我师傅来了我通知你,病人就离开了诊室。这里医生调用了wait方法让病人等待,病人的家就是医生的等待池,病人离开诊室就是释放了锁,病人接到医生通知后要从新到诊室排队。若是调用了wait(day)方法让病人等待,就是告诉病人几天后再来。
4.医生的师傅回来了,师傅对医生说,你让以前等个人那些人都来吧。这里医生调用了notifyall方法,等待在家里的全部病人就到医院候诊区排队,谁先排上队看病人的本事。
5.医生的师傅回来了,师傅对医生说,你随机挑一个病人来吧。这里医生调用了notify方法,等待在家里病人其中一个运气比较好被选中了,他就到候诊区来排队了。java
注:安全
1.线程自己属性:sleep、join、yield方法多线程
2.多线程同步使用的Object属性:wait、notify、notifyAll是Object的方法(synchronized要针对资源object起做用,因此也要经过调用资源object的wait和notify方法实现锁的等待),结论wait和notify以及notifyall必需要在synchronized代码块中使用,不然Java虚拟机会生成 IllegalMonitorStateException。由于wait的目的是为了进入等待并释放锁。dom
3.在多线程同步的时候使用等待池和锁池ide
4.在单线程运行的时候可使用sleep、yield释放CPU控制权,进去就绪池函数
5.join是一个特例,thread.join()是要当前线程等待,直到thread运行完成。thread.join就是让“当前线程”(正在拥有CPU控制权的主线程)等待,thread线程运行结束时会经过native方法调用notify让主线程继续工做。this
实例:spa
下面咱们提供一个使用wait和notify的范例程序。在这个程序里,咱们使用了上文所述的一些代码规范。咱们有两个线程,分别名为PRODUCER(生产者)和CONSUMER(消费者),他们分别继承了了Producer和Consumer类,而Producer和Consumer都继承了Thread类。Producer和Consumer想要实现的代码逻辑都在run()函数内。Main线程开始了生产者和消费者线程,并声明了一个LinkedList做为缓冲区队列(在Java中,LinkedList实现了队列的接口)。生产者在无限循环中持续往LinkedList里插入随机整数直到LinkedList满。咱们在while(queue.size == maxSize)循环语句中检查这个条件。请注意到咱们在作这个检查条件以前已经在队列对象上使用了synchronized关键词,于是其它线程不能在咱们检查条件时改变这个队列。若是队列满了,那么PRODUCER线程会在CONSUMER线程消耗掉队列里的任意一个整数,并用notify来通知PRODUCER线程以前持续等待。在咱们的例子中,wait和notify都是使用在同一个共享对象上的。线程
import java.util.LinkedList; import java.util.Queue; import java.util.Random; /** * Simple Java program to demonstrate How to use wait, notify and notifyAll() * method in Java by solving producer consumer problem. * * @author Javin Paul */ public class ProducerConsumerInJava { public static void main(String args[]) { System.out.println("How to use wait and notify method in Java"); System.out.println("Solving Producer Consumper Problem"); Queue<Integer> buffer = new LinkedList<>(); int maxSize = 10; Thread producer = new Producer(buffer, maxSize, "PRODUCER"); Thread consumer = new Consumer(buffer, maxSize, "CONSUMER"); producer.start(); consumer.start(); } } /** * Producer Thread will keep producing values for Consumer * to consumer. It will use wait() method when Queue is full * and use notify() method to send notification to Consumer * Thread. * * @author WINDOWS 8 * */ class Producer extends Thread { private Queue<Integer> queue; private int maxSize; public Producer(Queue<Integer> queue, int maxSize, String name){ super(name); this.queue = queue; this.maxSize = maxSize; } @Override public void run() { while (true) { synchronized (queue) { while (queue.size() == maxSize) { try { System.out .println("Queue is full, " + "Producer thread waiting for " + "consumer to take something from queue"); queue.wait(); } catch (Exception ex) { ex.printStackTrace(); } } Random random = new Random(); int i = random.nextInt(); System.out.println("Producing value : " + i); queue.add(i); queue.notifyAll(); } } } } /** * Consumer Thread will consumer values form shared queue. * It will also use wait() method to wait if queue is * empty. It will also use notify method to send * notification to producer thread after consuming values * from queue. * * @author WINDOWS 8 * */ class Consumer extends Thread { private Queue<Integer> queue; private int maxSize; public Consumer(Queue<Integer> queue, int maxSize, String name){ super(name); this.queue = queue; this.maxSize = maxSize; } @Override public void run() { while (true) { synchronized (queue) { while (queue.isEmpty()) { System.out.println("Queue is empty," + "Consumer thread is waiting" + " for producer thread to put something in queue"); try { queue.wait(); } catch (Exception ex) { ex.printStackTrace(); } } System.out.println("Consuming value : " + queue.remove()); queue.notifyAll(); } } } }
为了更好地理解这个程序,我建议你在debug模式里跑这个程序。一旦你在debug模式下启动程序,它会中止在PRODUCER或者CONSUMER线程上,取决于哪一个线程占据了CPU。由于两个线程都有wait()的条件,它们必定会中止,而后你就能够跑这个程序而后看发生什么了(颇有可能它就会输出咱们以上展现的内容)。你也可使用Eclipse里的Step into和Step over按钮来更好地理解多线程间发生的事情。
notify()和notifyAll()的本质区别
notify()和notifyAll()都是Object对象用于通知处在等待该对象的线程的方法。二者的最大区别在于:
notifyAll使全部原来在该对象上等待被notify的全部线程通通退出wait的状态,变成等待该对象上的锁,一旦该对象被解锁,他们就会去竞争。
notify则文明得多,它只是选择一个wait状态线程进行通知,并使它得到该对象上的锁,但不惊动其余一样在等待被该对象notify的线程们,当第一个线程运行完毕之后释放对象上的锁此时若是该对象没有再次使用notify语句,则即使该对象已经空闲,其余wait状态等待的线程因为没有获得该对象的通知,继续处在wait状态,直到这个对象发出一个notify或notifyAll,它们等待的是被notify或notifyAll,而不是锁。
1. 你可使用wait和notify函数来实现线程间通讯。你能够用它们来实现多线程(>3)之间的通讯。
2. 永远在synchronized的函数或对象里使用wait、notify和notifyAll,否则Java虚拟机会生成 IllegalMonitorStateException。
3. 永远在while循环里而不是if语句下使用wait。这样,循环会在线程睡眠先后都检查wait的条件,并在条件实际上并未改变的状况下处理唤醒通知。
4. 永远在多线程间共享的对象(在生产者消费者模型里即缓冲区队列)上使用wait。
5. 基于前文说起的理由,更倾向用 notifyAll(),而不是 notify()。
6.线程sleep结束后不是马上获取CPU时间片的,要从新等待CPU的时间片分配。
这是关于Java里如何使用wait, notify和notifyAll的全部重点啦。你应该只在你知道本身要作什么的状况下使用这些函数,否则Java里还有不少其它的用来解决同步问题的方案。例如,若是你想使用生产者消费者模型的话,你也可使用BlockingQueue,它会帮你处理全部的线程安全问题和流程控制。若是你想要某一个线程等待另外一个线程作出反馈再继续运行,你也可使用CycliBarrier或者CountDownLatch。若是你只是想保护某一个资源的话,你也可使用Semaphore。