采用信号量机制实现消费者与生产者的线程同步_Java线程通信
线程通信是Java线程部分的重点,我们介绍一下常见的几种线程通信方式。
线程锁与同步
锁机制是线程通信的一种重要方式。当多个线程竞争某一个对象时,一旦某个线程获得对象就会立刻将其上锁,其他线程只能等待锁被释放才可以继续竞争。当我们需要进行多个线程间的同步时,经常利用线程锁完成。
在下面的代码中,两个线程总会有一个执行先后顺序,但后执行的线程必须等待先执行的代码运行结束才可以执行。
public class ObjectLock { private static Object obj = new Object(); static class MyRunnableA implements Runnable { @Override public void run() { synchronized (obj) { for (int i = 1; i <= 100; ++i) System.out.println("MyRunnableA: " + i); } } } static class MyRunnableB implements Runnable { @Override public void run() { synchronized (obj) { for (int i = 1; i <= 100; ++i) System.out.println("MyRunnableB: " + i); } } } public static void main(String[] args) { new Thread(new MyRunnableA(), "线程1").start(); new Thread(new MyRunnableB(), "线程2").start(); }}
结果显示一个线程运行结束了以后才可以运行下一个线程。
等待/通知机制
等待/通知机制同样是线程通信方式中比较重要的方法,这里的等待使用的是Object类中的wait()方法,通知使用的是Object类中的notify()或notifyAll()方法,以上三个方法都必须在synchronized中使用。notify()方法一次只能随机唤醒一个等待线程,而notifyAll()会唤醒所有的等待线程。当线程使用wait()方法时,会自动释放占有的锁资源。我们用代码来查看一下。
public class WaitAndNotify { private static Object obj = new Object(); static class MyRunnableA implements Runnable { @Override public void run() { synchronized (obj) { for (int i = 0; i < 5; ++i) { System.out.println("MyRunnableA: " + i); obj.notify(); try { obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } obj.notify(); } } } static class MyRunnableB implements Runnable { @Override public void run() { synchronized (obj) { for (int i = 0; i < 5; ++i) { System.out.println("MyRunnableB: " + i); obj.notify(); try { obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } obj.notify(); } } } public static void main(String[] args) { new Thread(new MyRunnableA(), "线程1").start(); new Thread(new MyRunnableB(), "线程2").start(); }}
上面的代码交替等待和唤醒对方从而实现交替打印的效果,结果如下。
volatile共享内存
volatile关键字保证了线程间的内存可见性,本文暂不展开讲,后面会有专门的文章讲volatile和JMM(Java内存模型)相关的内容。
public class VolatileDemo { private static volatile boolean flag = true; static class MyRunnableA implements Runnable { @Override public void run() { System.out.println("start time: " + System.currentTimeMillis()); while (flag) ; System.out.println("flag已被置为false"); System.out.println("end time: " + System.currentTimeMillis()); } } static class MyRunnableB implements Runnable { @Override public void run() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("flag即将被置为false"); flag = false; } } public static void main(String[] args) { new Thread(new MyRunnableA(), "线程1").start(); new Thread(new MyRunnableB(), "线程2").start(); }}
以上的代码线程1初始时会阻塞在while循环里,但是线程2休眠1秒后将主内存中的flag改成了false,线程1第一时间获取了主内存的变量值变化并退出循环。具体结果如下:
管道
Java提供了四个类来实现管道,分别是PipedReader、PipedWriter、PipedInputStream和PipedOutputStream。前两个是面向字符的,后两个是面向字节的。下面的代码实现了一个简单的管道通信,模拟了C/S结构的通信。
public class Pipeline { private static PipedReader reader = new PipedReader(); private static PipedWriter writer = new PipedWriter(); static class Server implements Runnable { @Override public void run() { try { int receivedData = 0; while ((receivedData = reader.read()) != -1) { System.out.print((char)receivedData); } System.out.println(); } catch (IOException e) { e.printStackTrace(); } finally { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } } static class Client implements Runnable { @Override public void run() { try { writer.write("test"); } catch (IOException e) { e.printStackTrace(); } finally { try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } } } public static void main(String[] args) throws InterruptedException, IOException { writer.connect(reader); new Thread(new Server()).start(); TimeUnit.SECONDS.sleep(1); new Thread(new Client()).start(); }}
信号量
学过操作系统的同学们一定对信号量的概念不陌生,在Java中有一个定义信号量的工具类Semaphore。我们以一个经典的生产者-消费者模型来测试一下这种通信方式。
public class SemaphoreDemo { static class Producer implements Runnable { Semaphore mutex; Semaphore empty; Semaphore full; public Producer(Semaphore mutex, Semaphore empty, Semaphore full) { this.mutex = mutex; this.empty = empty; this.full = full; } @Override public void run() { while (true) { try { System.out.println("生产一些东西"); TimeUnit.SECONDS.sleep(1); empty.acquire(); mutex.acquire(); System.out.println("将数据放入缓冲中"); TimeUnit.SECONDS.sleep(1); mutex.release(); full.release(); } catch (InterruptedException e) { e.printStackTrace(); } } } } static class Consumer implements Runnable { Semaphore mutex; Semaphore empty; Semaphore full; public Consumer(Semaphore mutex, Semaphore empty, Semaphore full) { this.mutex = mutex; this.empty = empty; this.full = full; } @Override public void run() { while (true) { try { full.acquire(); mutex.acquire(); System.out.println("将数据从缓冲中取出"); TimeUnit.SECONDS.sleep(1); mutex.release(); empty.release(); System.out.println("消费一些东西"); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { Semaphore mutex = new Semaphore(1); Semaphore empty = new Semaphore(10); Semaphore full = new Semaphore(0); new Thread(new Producer(mutex, empty, full)).start(); new Thread(new Consumer(mutex, empty, full)).start(); }}
上面的代码模拟了经典的生产者-消费者模型,生产者一直在生产产品,消费者一直在消费产品,我们可以得到下面的运行结果:
采用信号量机制实现消费者与生产者的线程同步_Java线程通信相关推荐
- 采用信号量机制实现消费者与生产者的线程同步_你还能聊聊常用的进程同步算法? 上篇[五]...
点击上方 " 布衣码农 " ,免费订阅~选择" 设为星标 ",第一时间免费获得更新~ 「布衣码农」进程同步的最佳实践! 进程同步回顾 进程同步控制有多种方式:算 ...
- c++ linux 线程等待与唤醒_Linux线程同步(互斥量、信号量、条件变量、生产消费者模型)...
为什么要线程同步? 线程间有很多共享资源,都对一个共享数据读写操作,线程操作共享资源的先后顺序不确定,可能会造成数据的冲突 看一个例子 两个线程屏行对全局变量count++ (采用一个val值作为中间 ...
- 判断sem信号量为零_Linux线程同步(互斥量、信号量、条件变量、生产消费者模型)...
为什么要线程同步? 线程间有很多共享资源,都对一个共享数据读写操作,线程操作共享资源的先后顺序不确定,可能会造成数据的冲突 看一个例子 两个线程屏行对全局变量count++ (采用一个val值作为中间 ...
- 操作系统 之 「信号量机制解决进程同步问题」
文章目录 经典的信号量同步问题 第一部分 生产者消费者问题 1.多生产者多消费者 -- 吃水果 2.单生产者多消费者问题 -- 吸烟者 分析 3.多生产者问题 -- 仓库存货物 分析 解答 4.多生产 ...
- 【操作系统·考研】信号量机制/PV操作
在操作系统引入进程后,一方面,系统中的多道程序可以并发执行,不仅有效改善资源利用率,而且显著提高系统的吞吐量.另一方面,如果不对多个进程的运行进行妥善管理,必然会因为这些进程对系统资源的无序争夺给系统 ...
- 【Java 并发编程】多线程、线程同步、死锁、线程间通信(生产者消费者模型)、可重入锁、线程池
并发编程(Concurrent Programming) 进程(Process).线程(Thread).线程的串行 多线程 多线程的原理 多线程的优缺点 Java并发编程 默认线程 开启新线程 `Ru ...
- 操作系统基础-信号量机制的理解
文章目录 初步了解信号量 临界资源 临界区 进程互斥 进程同步 初步了解信号量 信号量机制的提出,是为了解决进程间关系通信的问题,因为进程间不可能用嘴来说"我在使用这个资源啊,你先等我用完再 ...
- Java高级-线程同步机制实现
2019独角兽企业重金招聘Python工程师标准>>> 前言 我们可以在计算机上运行各种计算机软件程序.每一个运行的程序可能包括多个独立运行的线程(Thread). 线程(Threa ...
- java线程 锁_Java多线程(二) 多线程的锁机制
当两条线程同时访问一个类的时候,可能会带来一些问题.并发线程重入可能会带来内存泄漏.程序不可控等等.不管是线程间的通讯还是线程共享数据都需要使用Java的锁机制控制并发代码产生的问题.本篇总结主要著名 ...
最新文章
- 更改git bash默认的路径
- Python中的yield生成器的简单介绍
- kgtemp文件转换mp3_amr转换mp3格式文件
- java 字符码_Java字符编码
- c语言中用简易暗纹来输入密码,确定夫琅和费单缝衍射明、暗纹位置的不同教学方法的讨论...
- 论文浅尝 | Multilingual LAMA: 探索多语言预训练语言模型中的知识
- UI设计实用干货素材|引导页模板
- 计划任务文件 linux,Linux计划任务Crontab学习笔记(3):配置文件
- bboss session自定义session id生成机制介绍
- opencv中遍历图片数据的两种方法
- 接口”安全机制”的设计
- 计算机键盘的认识,认识键盘和使用键盘的方法
- python 提取字幕_使用Python从zimuku下载字幕
- HCI超融合供应商全球六强对比
- python mysqldb_python mysqldb 教程
- 书单丨压箱底儿的10本传世计算机经典著作
- 【C语言必经之路——第13节】C语言中的数据类型详解
- python多线程并发编程技术_三 python并发编程之多线程-理论
- 淘宝店适合什么样引流方法?淘宝店应该如何引流?
- 最新Whatsns内容付费SEO优化带采集和熊掌号运营问答系统
热门文章
- 设计灵感|春节新年到!充满年味的海报给你参考
- 电商美工需要的素材PSD分层模板
- 设计师必备各类型3D字体图层样式PSD素材
- 零基础小白学习UI设计的4个步骤
- 正版python怎么下载_怎么下载官网python并安装
- Python爬虫项目---从wiley网站批量下载文章
- Windows x64平台 获取PEB表,并获取kernel32.dll的基址,并获取它的函数
- 基于GitHub创建自己的个人网站
- 动态头像 Android 实现,Android仿京东金融首页头像效果
- kali linux 里vim如何使用_Linux vim基本的使用方法