目录
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多线程的实现方式,包括继承Thread类和实现Runnable接口。 锁:Java中的锁机制,包括...线程间通信:Java中的线程间通信,包括wait()、notify()、notifyAll()等方法。
Java Core Sprout:一个萌芽阶段的Java核心知识库。 ...常用集合 数组列表/向量 链表 哈希映射 哈希集 ...深入理解线程通信 一个线程召集的诡异事件 线程池中你不可错过的一些细节 『ARM包入坑指北』之队列
线程通信 为什么要线程通信? 多个线程并发执行时,在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务,并且我们希望他们有规律的执行,那么多线程之间需要一些协调通信,以此来帮我们达到...
资源名称:Java多线程与并发库高级应用视频教程22集资源目录:【】01传统线程技术回顾【】02传统定时器技术回顾【】03传统线程互斥技术【】04传统线程同步通信技术【】04传统线程同步通信技术_分割纪录【】05线程...
Java并发编程的核心概念包括: 线程(Thread):线程是程序执行流的最小单元。一个进程可以有一个或多个线程,每个线程都执行其自己的任务。 进程(Process):进程是操作系统分配资源(如内存)和调度的单位。一个...
java多线程并发控制通信,用hibernate存储信息,数据库mysql.
用途: 理解Java中的多线程编程和数据库操作,提高程序的并发性和数据存储能力。 实验六: 关键词: 网络Socket编程(选择) 内容关键词: 网络Socket编程,Java实验 用途: 学习Java中的网络编程,了解Socket通信原理和...
线程同步与通信:掌握Java中的同步机制,如synchronized关键字、wait()和notify()方法,以及更高级的并发工具如ReentrantLock、Condition等。了解线程间的通信方式,如共享内存、消息传递等。 并发集合:熟悉Java...
Java并发编程的艺术 作者:方腾飞 魏鹏 程晓明 著 丛书名:Java核心技术系列 出版日期 :2015-07-25 ISBN:978-7-111-50824-3 第1章介绍Java并发编程的挑战,向读者说明进入并发编程的世界可能会遇到哪些问题,以及如何...
第三部分详细、深入地介绍volatile关键字的语义,volatile关键字在Java中非常重要,可以说它奠定了Java核心并发包的高效运行,在这一部分中,我们通过实例展示了如何使用volatile关键字以及非常详细地介绍了Java内存...
生产者如果不释放对临界资源的占用权,那么 消费者就无法消费队列中的商品 ,就不会让队列有空间,那么生产者就会一直无限等待下去。因此,一般情况下,当队列满时,会让生产...这种互相通信的过程就是线程间的协作。
第49节Java中的阻塞队列原理与使用00:26:18分钟 | 第50节实战:简单实现消息队列00:11:07分钟 | 第51节并发容器ConcurrentHashMap原理与使用00:38:22分钟 | 第52节线程池的原理与使用00:42:49分钟 | 第53节...
在并发编程中,需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体)。通信是指线程之间以何种机制来交换信息。在命令式编程中,线程之间的通信机制有两种:共享内存和...
对于一个进程中的多个线程来说,多个线程共享进程的内存块,当有新的线程产生的时候,操作系统不分配新的内存,而是让新线程共享原有的进程块的内存。因此,线程间的通信很容易,速度也很快。不同的进程因为处于不同...
龙果 java并发编程原理实战 第2节理解多线程与并发的之间的联系与区别 [免费观看] 00:11:59分钟 | 第3节解析多线程与多进程的联系以及上下文切换所导致资源浪费问题 [免费观看] 00:13:03分钟 | 第4节学习并发的四...
常用集合 数组列表/向量 链表 哈希映射 哈希集 链接哈希映射 Java多线程 多线程中的常见问题 同步关键字原理 ...深入理解线程通信 一个线程召集的诡异事件 线程池中你不可错过的一些细节 『ARM包入坑指北』之队列
结合大量实例,全面讲解Java多线程编程中的并发访问、线程间通信、锁等最难突破的核心技术与应用实践 Java多线程无处不在,如服务器、数据库、应用。多线程可以有效提升计算和处理效率,大大提升吞吐量和可伸缩性,...
本程序利用了Java多线程进行了TCP的端口扫描,能够满足一般的业务需求,适用与网管或网络安全从事者