前言
前面一节中我们学习如何在多线程中确保共享数据的线程安全。本节我们将学一下如何实现多线程间的通信。
首先在JAVA中有很多方式实现多线程间的通信。本节将着重讲学习如何使用synchronized+notify+wait+flag的方式来实现多线程的通信。
首先我们先了解一下一些概念:
- wait()
- 令当前线程挂起并放弃CPU、同步资源,使别的线程可访问并修改共享资源,而当前线程排队等候再次对资源的访问
- notify()
- 唤醒正在排队等待同步资源的线程中优先级最高者结束等待。
- notifyAll ()
- 唤醒正在排队等待资源的所有线程结束等待。
wait()、notify()、notifyAll () 只能在synchronized修饰的代码块或者方法中使用!,是多线程通信的独有方法。
示例
我们将通过一些例子来学习java多线程通信。
生产者与消费者
生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下, 如果店中有产品了再通知消费者来取走产品。class Clerk{//店员 int product; public synchronized void addProduct(){//生产产品 if(product >= 20){ try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }else{ product++; System.out.println(Thread.currentThread().getName() + ":生产了第" + product + "个产品"); notifyAll(); } } public synchronized void consumeProduct(){//消费产品 if(product <= 0){ try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }else{ System.out.println(Thread.currentThread().getName() + ":消费了第" + product + "个产品"); product--; notifyAll(); } } } class Producer implements Runnable{//生产者 Clerk clerk; public Producer(Clerk clerk){ this.clerk = clerk; } public void run(){ System.out.println("生产者开始生产产品"); while(true){ try { Thread.currentThread().sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } clerk.addProduct(); } } } class Consumer implements Runnable{//消费者 Clerk clerk; public Consumer(Clerk clerk){ this.clerk = clerk; } public void run(){ System.out.println("消费者消费产品"); while(true){ try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } clerk.consumeProduct(); } } } public class produceAndConsumer{ public static void main(String[] args) { Clerk clerk = new Clerk(); Producer p1 = new Producer(clerk); Consumer c1 = new Consumer(clerk); Thread t1 = new Thread(p1);//一个生产者的线程 Thread t2 = new Thread(c1);//一个消费者的线程 t1.setName("生产者1"); t2.setName("消费者1"); t1.start(); t2.start(); } }
程序设计思路:在上例中,生产者和消费者只负责生产产品和消费产品,至于什么时候(库存>=0)该生产什么时候(0<库存<20)可以消费取决于店员,因而店员将是统一调度生产者生产和消费者消费的关键所在。
两个打印机交替打印
- 两个打印机交替打印1-100内的整数
代码示例
``` package coreJavaReview.thread; class PrintNum implements Runnable { int num = 1; Object obj = new Object(); public void run() { while (true) { synchronized (obj) { obj.notify(); if (num <= 100) { try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":" + num); num++; } else { break; } try { obj.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } } public class ThreadCommunicationCross { public static void main(String[] args) { PrintNum p = new PrintNum(); Thread t1 = new Thread(p); Thread t2 = new Thread(p); t1.setName("A"); t2.setName("B"); t1.start(); t2.start(); } } ```
程序设计思路:两台打印机交替打印,每次只能有一台机器在打印,也需要要求是多线程安全的,此时要求获得打印资格的打印机必须在打印完毕之后放弃(放弃CPU资源)打印资格。
多线程按照顺序循环地执行
- 场景说明
开启三个线程顺序地执行输出1-150整数。,每个线程持续输出连续的5个整数。 代码示例
package coreJavaReview.thread; public class ThreadCrossCommunicationTest { public static void main(String[] args) { Object obj = new Object(); for(int i=1;i<=3;i++){//传入对象锁 和 i值 new Thread(new MyThread2(obj,i), "线程"+i).start(); } } } class MyThread2 implements Runnable{ private static int i = 0;//打印的值 1-75 private static int count=0;//计数 三次一轮回 private Object obj; private int n;//接参 i值 public MyThread2(Object obj,int n) { this.obj=obj; this.n = n; } @Override public void run() { synchronized (obj) { while(i<150){//i++ 在代码块 所以到74就可以了 obj.notifyAll();//唤醒所有线程 if(count%3!=(n-1)){ //找出下一个线程 不正确的线程等待 try { obj.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } i++; System.out.println(Thread.currentThread().getName()+": "+i); if(i%5 == 0){ //打印了五次后 此线程让出资源,等待 try { count++; //count是static修饰 ,为了共享 System.out.println(); obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }
- 场景说明