线程通信是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线程通信相关推荐

  1. 采用信号量机制实现消费者与生产者的线程同步_你还能聊聊常用的进程同步算法? 上篇[五]...

    点击上方 " 布衣码农 " ,免费订阅~选择" 设为星标 ",第一时间免费获得更新~ 「布衣码农」进程同步的最佳实践! 进程同步回顾 进程同步控制有多种方式:算 ...

  2. c++ linux 线程等待与唤醒_Linux线程同步(互斥量、信号量、条件变量、生产消费者模型)...

    为什么要线程同步? 线程间有很多共享资源,都对一个共享数据读写操作,线程操作共享资源的先后顺序不确定,可能会造成数据的冲突 看一个例子 两个线程屏行对全局变量count++ (采用一个val值作为中间 ...

  3. 判断sem信号量为零_Linux线程同步(互斥量、信号量、条件变量、生产消费者模型)...

    为什么要线程同步? 线程间有很多共享资源,都对一个共享数据读写操作,线程操作共享资源的先后顺序不确定,可能会造成数据的冲突 看一个例子 两个线程屏行对全局变量count++ (采用一个val值作为中间 ...

  4. 操作系统 之 「信号量机制解决进程同步问题」

    文章目录 经典的信号量同步问题 第一部分 生产者消费者问题 1.多生产者多消费者 -- 吃水果 2.单生产者多消费者问题 -- 吸烟者 分析 3.多生产者问题 -- 仓库存货物 分析 解答 4.多生产 ...

  5. 【操作系统·考研】信号量机制/PV操作

    在操作系统引入进程后,一方面,系统中的多道程序可以并发执行,不仅有效改善资源利用率,而且显著提高系统的吞吐量.另一方面,如果不对多个进程的运行进行妥善管理,必然会因为这些进程对系统资源的无序争夺给系统 ...

  6. 【Java 并发编程】多线程、线程同步、死锁、线程间通信(生产者消费者模型)、可重入锁、线程池

    并发编程(Concurrent Programming) 进程(Process).线程(Thread).线程的串行 多线程 多线程的原理 多线程的优缺点 Java并发编程 默认线程 开启新线程 `Ru ...

  7. 操作系统基础-信号量机制的理解

    文章目录 初步了解信号量 临界资源 临界区 进程互斥 进程同步 初步了解信号量 信号量机制的提出,是为了解决进程间关系通信的问题,因为进程间不可能用嘴来说"我在使用这个资源啊,你先等我用完再 ...

  8. Java高级-线程同步机制实现

    2019独角兽企业重金招聘Python工程师标准>>> 前言 我们可以在计算机上运行各种计算机软件程序.每一个运行的程序可能包括多个独立运行的线程(Thread). 线程(Threa ...

  9. java线程 锁_Java多线程(二) 多线程的锁机制

    当两条线程同时访问一个类的时候,可能会带来一些问题.并发线程重入可能会带来内存泄漏.程序不可控等等.不管是线程间的通讯还是线程共享数据都需要使用Java的锁机制控制并发代码产生的问题.本篇总结主要著名 ...

最新文章

  1. 更改git bash默认的路径
  2. Python中的yield生成器的简单介绍
  3. kgtemp文件转换mp3_amr转换mp3格式文件
  4. java 字符码_Java字符编码
  5. c语言中用简易暗纹来输入密码,确定夫琅和费单缝衍射明、暗纹位置的不同教学方法的讨论...
  6. 论文浅尝 | Multilingual LAMA: 探索多语言预训练语言模型中的知识
  7. UI设计实用干货素材|引导页模板
  8. 计划任务文件 linux,Linux计划任务Crontab学习笔记(3):配置文件
  9. bboss session自定义session id生成机制介绍
  10. opencv中遍历图片数据的两种方法
  11. 接口”安全机制”的设计
  12. 计算机键盘的认识,认识键盘和使用键盘的方法
  13. python 提取字幕_使用Python从zimuku下载字幕
  14. HCI超融合供应商全球六强对比
  15. python mysqldb_python mysqldb 教程
  16. 书单丨压箱底儿的10本传世计算机经典著作
  17. 【C语言必经之路——第13节】C语言中的数据类型详解
  18. python多线程并发编程技术_三 python并发编程之多线程-理论
  19. 淘宝店适合什么样引流方法?淘宝店应该如何引流?
  20. 最新Whatsns内容付费SEO优化带采集和熊掌号运营问答系统

热门文章

  1. 设计灵感|春节新年到!充满年味的海报给你参考
  2. 电商美工需要的素材PSD分层模板
  3. 设计师必备各类型3D字体图层样式PSD素材
  4. 零基础小白学习UI设计的4个步骤
  5. 正版python怎么下载_怎么下载官网python并安装
  6. Python爬虫项目---从wiley网站批量下载文章
  7. Windows x64平台 获取PEB表,并获取kernel32.dll的基址,并获取它的函数
  8. 基于GitHub创建自己的个人网站
  9. 动态头像 Android 实现,Android仿京东金融首页头像效果
  10. kali linux 里vim如何使用_Linux vim基本的使用方法