目录
1.wait(),notify(),notifyAll()
2.生产者消费者模式
3.多生产多消费模式
1.wait(),notify(),notifyAll()
Object类为我们定义了线程通信的方法,如wait(),notify()等,这些方式是本地的而且是final的.
1.1wait()
1)调用wait()方法,能让当前线程阻塞并交出此对象的monitor,然后进入等待状态直到其他线程调用此对象的notify()或notifyAll()方法.当前的线程必须拥有此对象的monitor,也就是说wait()方法需要在Synchronized域内使用.
2)wait()和sleep()的区别
wait | sleep | |
所属类 | Thread | Object |
对象锁 | 释放 | 不释放 |
使用环境 | synchronized域内 | 任意环境 |
唤醒方式 | 通过notify或notifyAll唤醒 | 休眠到指定时间自动唤醒 |
1.2notify(),notifyAll()
调notify()方法能够唤醒一个正在等待这个对象的monitor的某一个线程,notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程.同样的,这两个方法需要在Synchronized域内使用,也不会释放锁,.
2.生产者消费者模式
生产者消费者模式是经典的线程通信模式,其主旨为两个线程交替对一个共享资源进行操作,并相互进行通信.例:
public class ThreadDemo { public static void main(String[] args) { Resource r = new Resource(); new Thread(new Producer(r)).start(); new Thread(new Consumer(r)).start(); } } // 资源类 class Resource { private boolean flag = false; private int count = 0; public synchronized void put() { if (flag) { try { wait(); } catch (InterruptedException e) { } } count++; System.out.println("生产者行为..." + count); flag = true; notify(); } public synchronized void get() { if (!flag) { try { wait(); } catch (InterruptedException e) { } } System.out.println("消费者行为......" + count); flag = false; notify(); } } // 生产者线程 class Producer implements Runnable { private Resource r; public Producer(Resource r) { this.r = r; } @Override public void run() { while (true) { r.put(); } } } // 消费者线程 class Consumer implements Runnable { private Resource r; public Consumer(Resource r) { this.r = r; } @Override public void run() { while (true) { r.get(); } } }
运行结果:生产者消费者交替执行,每次对应的行为计数(count)相同.
3.多生产多消费模式
现实生活中长对应多个生产者和多个消费者,因此我们可以增加线程来实现.在上一节的main()函数内增加多个线程,例:
public class ThreadDemo { public static void main(String[] args) { Resource r = new Resource(); new Thread(new Producer(r)).start(); new Thread(new Producer(r)).start(); new Thread(new Producer(r)).start(); new Thread(new Producer(r)).start(); new Thread(new Producer(r)).start(); new Thread(new Consumer(r)).start(); new Thread(new Consumer(r)).start(); new Thread(new Consumer(r)).start(); new Thread(new Consumer(r)).start(); new Thread(new Consumer(r)).start(); } }
运行结果:生产者消费者执行过程中偶尔会执行多次同一行为:
生产者行为...41133
消费者行为......41133
消费者行为......41133
我们已经使用了synchronized了,究竟是哪里出错了呢?让我们重新来看消费者的行为代码:
public synchronized void get() { if (!flag) {// 1 try { wait();// 2 } catch (InterruptedException e) { } } System.out.println("消费者行为......" + count);// 3 flag = false;// 4 notify();// 5 }
假设消费者C1调用了get()方法并得到了当前对象的锁,执行代码第1行进行if判断>>>如果if语句判断为true,C1进入等待状态并释放当前对象锁>>>当其他线程调用了notify()唤醒C1后,C1继续执行之前的任务,执行代码第345行.问题就是在这里出现的,因为C1再次执行时没有进行flag标志位的判断而继续执行,而如果此时flag标志位不满足条件时,打印的数据就是错的.为了使线程再被唤醒后能再次对标志位进行判断,我们可以将if语句改为while语句.例:
public synchronized void get() { while (!flag) {// 1 try { wait();// 2 } catch (InterruptedException e) { } } System.out.println("消费者行为......" + count);// 3 flag = false;// 4 notify();// 5 }
修改后再次运行,程序运行一会后阻塞了,又是为什么呢?再次分析修改后的代码:
消费者C1正常执行后调用了notify()方法>>>由于notify()方法只能唤醒某一个等待线程,如果唤醒的是另一个消费者C2,C2获得执行权>>>由于flag标志位没变,因此C2也会进入等待状态.但是如果这是最后一个非等待状态的线程,那么所有线程都会处于wait()状态从而阻塞.也就是说,这次是notify()引起的问题,如果notify()唤醒的是本方线程,那么是没有意义的,因此我们可以使用notifyAll()唤醒所有线程,从而达到唤醒对方线程的目的.再次修改后的代码:
public synchronized void put() { while (flag) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } count++; System.out.println(Thread.currentThread().getName() + "生产者行为..." + count); flag = true; notifyAll(); } public synchronized void get() { while (!flag) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + "消费者行为......" + count); flag = false; notifyAll(); }
运行结果:多生产者之间和多消费者之间是随机执行的,但每一个生产者和一个消费者对应并执行同样的计数行为.
总结:在多生产多消费模式中,需通过while判断和notifyAll()唤醒所有线程,以实现通信功能.notifyAll()虽然达到了唤醒对方的目的,但同时也唤醒了所有本方的线程,因此也是影响性能的,在后面的高级并发对象中我们会解决这样的问题.
相关推荐
Java并发编程中的线程基础是理解多线程编程的关键,它涵盖了从线程的创建到管理,再到线程间通信的全过程。多线程编程在处理高性能和高响应性的应用程序时至关重要,因为它允许程序在单个进程中同时执行多个任务,...
《Java并发编程:设计原则与模式》是一本深入探讨Java多线程编程的书籍,它涵盖了并发编程中的关键概念、原则和模式。在Java中,并发处理是优化应用程序性能、提高资源利用率的重要手段,尤其在现代多核处理器的环境...
在Java编程中,多线程并发是提升程序执行效率、充分利用多核处理器资源的重要手段。本文将基于"java 多线程并发实例"这个主题,深入探讨Java中的多线程并发概念及其应用。 首先,我们要了解Java中的线程。线程是...
《Java并发编程:设计原则与模式(第二版)》是一本深入探讨Java平台上的多线程和并发编程的权威著作。这本书旨在帮助开发者理解和掌握如何有效地编写可扩展且高效的并发程序。以下是书中涵盖的一些关键知识点: 1....
Java中的并发编程是一个复杂的主题,需要对线程生命周期的每个阶段都有清晰的认识,才能编写出既能充分利用多核CPU资源,又能稳定运行的高效程序。在实际开发中,我们需要考虑到线程间的同步、并发控制、线程池的...
在Java编程中,多线程通信是一个至关重要的概念,特别是在设计高效的并发应用程序时。这个"Java线程通信示例源代码"很可能包含了演示如何在不同线程之间共享数据和协调执行顺序的实例。线程通信主要涉及两个核心概念...
6. **线程通信**:讲述了wait()、notify()和notifyAll()方法以及它们在实现线程间通信中的角色。同时,还讨论了高级的阻塞队列(BlockingQueue)及其在生产者-消费者模型中的应用。 7. **并发设计模式**:介绍了...
书中还涉及了其他重要主题,如线程通信、线程池的最佳实践、内存模型和可见性、原子变量、并发集合等。通过学习这些设计原则和模式,开发者能够更好地应对Java并发编程的挑战,编写出高效、可靠的并发应用程序。阅读...
1. **并发基础**:首先,书中会介绍并发编程的基本概念,如线程、进程、同步与通信机制,以及Java中的线程API,如`Thread`类和`Runnable`接口。 2. **线程管理**:讨论如何创建、启动、停止和控制线程,包括线程池...
线程之间的协作是多线程编程中的另一个关键方面,涉及到线程之间的通信和同步。Java提供了多种机制来实现这一点: 1. **Wait/Notify**: `Object.wait()`和`Object.notify()`用于线程之间的简单同步。 2. **...
总之,Java并发编程涉及线程的创建、同步、通信和管理等多个方面。通过理解并熟练掌握`synchronized`、`volatile`、`ExecutorService`、`Future`等核心概念,开发者能够构建高效、稳定的并发程序,应对复杂的应用...
在Java中,线程通信主要依赖于共享内存和内置的等待/通知机制。 首先,线程通过共享对象通信是一种常见的策略。例如,一个线程(线程A)可能负责生产数据,而另一个线程(线程B)则负责处理这些数据。线程A可以通过...
`ExecutorService`是Java并发工具包中的核心接口之一,它定义了一组线程池管理的方法,可以帮助我们更加高效地管理和调度线程。 - **固定大小的线程池**:`newFixedThreadPool(int nThreads)`创建一个可重用的固定...
Java TCP多线程通信是网络编程中的一个重要概念,它结合了Java的Socket编程与多线程技术,使得多个客户端能够同时与服务器进行交互。在Java中,TCP(传输控制协议)提供了一种可靠的数据传输方式,确保数据的有序、...
根据提供的信息,“Java 并发编程实战.pdf”这本书聚焦于Java并发编程的实践与应用,旨在帮助读者深入了解并掌握Java中的多线程技术及其在实际项目中的应用技巧。虽然部分内容未能提供具体章节或实例,但从标题及...
1. `wait()`, `notify()`: 这些方法用于对象级别的线程通信,使得线程可以进入等待状态并等待其他线程的唤醒。 2. `join()`: 一个线程可以调用另一个线程的`join()`方法,直到另一个线程完成其执行。 3. `...
在《java线程与并发实践编程》中,作者Jeff Friesen可能还会深入讨论线程池的配置策略、死锁和活锁的预防、线程性能分析与调优,以及Java内存模型(JMM)和线程通信模型(如wait()、notify()、notifyAll())等内容。...
9. **线程通信**:`wait()`, `notify()`和`notifyAll()`方法是Java中基本的线程通信手段,书中会讲解如何利用它们实现线程间的协作。 10. **死锁、活锁与饥饿**:这些是并发编程中的常见问题,书中会分析它们产生的...
- **线程通信**:`wait()`, `notify()`, `notifyAll()`是用于线程间通信的方法,但必须在`synchronized`块或方法中使用。 2. **并发工具类** - **ExecutorService**:Java并发框架中的核心组件,用于管理线程池,...