精华内容
下载资源
问答
  • Java线程管道通讯

    千次阅读 2015-01-20 10:43:25
    操作系统对于管道的吹嘘往往是天花龙凤, 好心点的就贴段伪代码给你看,为出书而出书的,就直接一堆概念堆在上面,让人根本看不懂, 如此简单的概念,明明几句话就解释清楚,有的书还专门开出一章来讨论这个问题...

    很多操作系统对于管道的吹嘘往往是天花龙凤,

    好心点的就贴段伪代码给你看,为出书而出书的,就直接一堆概念堆在上面,让人根本看不懂,

    如此简单的概念,明明几句话就解释清楚,有的书还专门开出一章来讨论这个问题,完全没有必要!


    一、基本概念

    其实管道的概念非常简单,就是连接两个线程通讯的缓冲区,画个图就更加明白了


    写者进程把自己的数据通过管道输出流写入管道,读者进程再从管道通过管道输入流拿管道里面的数据

    当然进程与进程之间传递数据未必通过这个方式去传递数据,

    完全可以在一个进程中设置一个public变量,然后再另一个进程用.+成员变量访问,当然这样可能会被批什么耦合度高的鬼东西

    管道在线程之间传递数据传递数据,只是其中一个方法,当然如果你要考研,或者考操作系统,一定要好好掌握这个内容,其实也不难,就是一个简单的Java程序,简单得都不知道有什么用~


    二、基本目标

    有这样的一个程序:


    写者进程每250毫秒工作一次,不停地对管道输出数据,直到输出到10,

    读者则每500毫秒工作一次,不停地从管道读取数据,直到读完

    如果读者比写者工作速度快,也就是读者请求第一个数据,写者还没开始写,本来我以为这样要出错的,经过试验,发现一旦管道输入输出流建立起来之后,不加上close()方法,读者必须等待写者写出数据才读,直到写者写完所有为止。

    然后,我还以为管道通讯,必须双开管道流才不会出错,结果发现关掉其中一头,程序是不会报错的,只是一头使劲在写/读,直到写/读完。

    最后,我还试能否有两个读者从管道中取数据,结果发现是不行的,管道必须是一对一的,假如有一个写者,多个读者,就只有一个读者能够读到管道的所有数据,而其余读者什么都读不到。


    三、制作过程

    注意先在头部引入java.io.*包由于用到了输入输出流

    1、首先是主函数:

    public class PipeThread {
    	public static void main(String args[]) throws IOException {
    		PipedOutputStream pos = new PipedOutputStream();
    		PipedInputStream pis = new PipedInputStream(pos);
    		new Writer(pos).start();
    		new Reader(pis).start();
    	}
    }

    管道输出流可以无参数创建,

    管道输入流必须根据管道输出流创建,否则Java会爆“管道流”无法建立的异常

    然后把管道输出流与管道输入流扔到写者进程与读者进程,这里两个进程必须用构造函数来实现这两个管道流的获取,不能再其中的run()进程设置参数,这是Java的默认参数


    2、然后是写者进程:

    class Writer extends Thread {
    	private PipedOutputStream pos;
    
    	public Writer(PipedOutputStream pos) {
    		this.pos = pos;
    	}
    
    	public void run() {
    		PrintStream p = new PrintStream(pos);
    		for (int i = 1; i < 11; i++) {
    			try {
    				Thread.currentThread().sleep(250);
    			} catch (Exception e) {
    			}
    			p.println(i);
    			System.out.println("Write:" + i);
    		}
    		System.out.println("已经写入完毕");
    		p.flush();
    		p.close();
    	}
    }
    一开始是写者的构造函数,声明这里用到的管道输出流就是主函数那个传过来的管道输出流,

    之后用打印流,每个250s把内容打印流到管道里面并输出到屏幕,至于什么是打印流,可以参考我之前的《【Java】打印流与缓冲区读者完成输入与输出到文件操作》(点击打开链接),然后Java进程怎么用,可以参考我之前的《【Java】线程并发、互斥与同步》(点击打开链接

    然后关闭打印流之前,用flush()清空一下内存里面的内容,其实不清,不关打印流也没有关系的,你的windows系统会自动处理这些东西的。


    3、最后是读进程:

    class Reader extends Thread {
    	private PipedInputStream pis;
    	private String line;
    
    	public Reader(PipedInputStream pis) {
    		this.pis = pis;
    	}
    
    	public void run() {
    		BufferedReader r = new BufferedReader(new InputStreamReader(pis));
    		try {
    			do {
    				line = r.readLine();
    				if (line != null)
    					System.out.println("Read:" + line);
    				else
    					System.out.println("已经读取完毕");
    				Thread.currentThread().sleep(500);
    			} while (r != null && line != null);
    		} catch (Exception e) {
    		}
    	}
    }

    一开始的构造函数保证了这个管道输入流,就是主函数那个已经与管道输出流连接起来的管道输入流

    缓冲区读者,从输入流不停地读数据,且这个输入流是从管道输入流来读取的,读完为止,至于什么是缓冲区读者,可以参考我之前的《【Java】打印流与缓冲区读者完成输入与输出到文件操作》(点击打开链接),打印流与缓冲区读者是Java中很常见的读、写机制,必须要弄懂。

    因此,整个程序如下:

    import java.io.*;
    
    class Writer extends Thread {
    	private PipedOutputStream pos;
    
    	public Writer(PipedOutputStream pos) {
    		this.pos = pos;
    	}
    
    	public void run() {
    		PrintStream p = new PrintStream(pos);
    		for (int i = 1; i < 11; i++) {
    			try {
    				Thread.currentThread().sleep(250);
    			} catch (Exception e) {
    			}
    			p.println(i);
    			System.out.println("Write:" + i);
    		}
    		System.out.println("已经写入完毕");
    		p.flush();
    		p.close();
    	}
    }
    
    class Reader extends Thread {
    	private PipedInputStream pis;
    	private String line;
    
    	public Reader(PipedInputStream pis) {
    		this.pis = pis;
    	}
    
    	public void run() {
    		BufferedReader r = new BufferedReader(new InputStreamReader(pis));
    		try {
    			do {
    				line = r.readLine();
    				if (line != null)
    					System.out.println("Read:" + line);
    				else
    					System.out.println("已经读取完毕");
    				Thread.currentThread().sleep(500);
    			} while (r != null && line != null);
    		} catch (Exception e) {
    		}
    	}
    }
    
    public class PipeThread {
    	public static void main(String args[]) throws IOException {
    		PipedOutputStream pos = new PipedOutputStream();
    		PipedInputStream pis = new PipedInputStream(pos);
    		new Writer(pos).start();
    		new Reader(pis).start();
    	}
    }
    


    展开全文
  • Java多线程通讯

    千次阅读 2017-10-19 17:36:23
    多线程之间通讯,其实就是多个线程在操作同一个资源,互相可以感知到其它线程的状态或行为。 线程通讯主要可以分为三种类型,分别为**共享内存**、**消息传递**和**管道流**。每种类型有不同的方法来实现。 - 共享...

    什么是多线程通讯?

    多线程之间通讯,其实就是多个线程在操作同一个资源,互相可以感知到其它线程的状态或行为。

    线程通讯主要可以分为三种类型,分别为共享内存消息传递管道流。每种类型有不同的方法来实现。

    • 共享内存:线程之间共享程序的公共状态,线程之间通过读-写内存中的公共状态来隐式通信。

      volatile共享内存

    • 消息传递:线程之间没有公共的状态,线程之间必须通过明确的发送信息来显示的进行通信。

      wait/notify等待通知方式
      join方式

    • 管道流

      管道输入/输出流的形式

      消息队列

    多线程之间的通讯需求

    有些复杂程序或者是系统需要多个进程或者线程共同完成某个具体的任务,那么也就需要进程之间通信和数据访问。比如会遇到需要所有子线程执行完毕通知主线程处理某些逻辑的场景,或者是线程 A 在执行到某个条件需要通知或者等待线程 B 执行某个操作等情况。

    线程是操作系统调度的最小单位,有自己的栈空间,可以按照既定的代码逐步的执行,但是如果每个线程间都孤立的运行,那就会造资源浪费。所以在现实中,我们需要这些线程间可以按照指定的规则共同完成一件任务,所以这些线程之间就需要互相协调,这个过程被称为线程的通信。

    线程是操作系统中独立的个体,但是这些个体如果不经过特殊处理就不能成为一个整体,线程间通信就成为整体的必用方式之一。

    多线程通讯模型

    在这里插入图片描述

    关于JMM的详细解析请参考这篇博文:一文看懂Java内存模型(JMM)

    多线程之间如何实现通讯

    系统要实现某个全局功能必定要需要各个子模块之间的协调和配合,就像一个团队要完成某项任务的时候需要团队各个成员之间密切配合一样。而对于系统中的各个子线程来说,如果要完成一个系统功能,同样需要各个线程的配合,这样就少不了线程之间的通信与协作。常见的线程之间通信方式有如下几种:

    1. 通过共享资源进行忙等待(Busy Wait)
    2. wait和notify/notifyAll
    3. await和signal/signalAll
    4. sleep/yield/join
    5. CyclicBarrier 栅栏
    6. CountDownLatch 闭锁
    7. Semaphore 信号量
    8. Socket套接字进行网络通信

    1、通过共享资源进行忙等待(Busy Wait)

    线程间发送信号的一个简单方式是直接在共享内存中操作对象。线程A在一个同步块里设置boolean型成员变量flag为true,线程B也在同步块里读取flag这个成员变量。

    准备处理数据的线程B正在等待数据变为可用。换句话说,它在等待线程A的一个信号,这个信号使getFlag()返回true。线程B运行在一个循环里,以等待这个信号。

    2、wait和notify/notifyAll

    wait和notify/notifyAll是Object的方法,任何一个对象都具有该方法。在使用的时候,首先需要设置一个全局锁对象,通过对该锁的释放和持有来控制该线程的运行和等待。因此在调用wait和notify的时候,该线程必须要已经持有该锁,然后才可调用,否则将会抛出IllegalMonitorStateException异常。
    确定要让哪个线程等待?让哪个线程等待就在哪个线程中调用锁对象的wait方法。调用wait等待的是当前线程,而不是被调用线程,并不是theread.wait()就可以让thread等待,而是让当前线程(实际执行wait方法的线程,而不是调用者的那个线程对象)进行等待。尽量不要把线程对象当做全局锁使用,以免混淆等待线程。

    在这里插入图片描述

    3、await和signal/signalAll

    await和signal是Condition的两个方法,其作用和wait和notify一样,目的都是让线程挂起等待,不同的是,这两种方法是属于Condition的两个方法,而Condition对象是由ReentrantLock调用newCondition()方法得到的。Condition对象就相当于前面所说的中介,在线程中调用contiton.await()和condition.signal()可以分别使线程等待和唤醒。

    4、sleep/yield/join

    对于sleep()方法应该很熟悉了,让当前线程睡眠一段时间。期间不会释放任何持有的锁

    对于yield()方法可能使用的情况少一下。其作用主要是让当前线程从运行状态转变为就绪状态,由线程调度重新选择就绪状态的线程分配CPU资源。至于最终会选取哪个线程分配CPU资源就由调度策略来决定了,有可能还是该线程,有可能换为其它线程。

    对于join方法,作用是暂停当前线程,等待被调用线程指向结束之后再继续执行。

    使用join的时候需要注意:

    1、调用join的时候,当前线程不会释放掉锁,如果调用线程也需要该锁则就会导致死锁!

    2、join方法不会启动调用线程,所以,在调用join之前,该调用线程必须已经start启动,否则不会达到想要的效果。

    join的底层实际是就是使用了一个自旋等待机制,判断调用线程是否死亡,如果没有则一直让当前线程wait。可以看一下底层实现源码:

    5、CyclicBarrier栅栏

    CyclicBarrier字面理解为线程屏障,当指定数量的线程执行到指定位置的时候,才能触发后续动作的进行。其最终目的是让所有线程同时开始后续的工作。

    例如:三个员工来公司开会,由于三人住的地方与公司距离不同,所以到会议室的时间也不同。而会议开始必须等待三者都到达会议室之后才能进行。

    6、CountDownLatch闭锁

    与CycliBarrier不同的是CountDownLatch是某一个线程等待其他线程执行到某一位置之后,该线程(调用countDownLatch.await();等待的线程)才会继续后续工作。而CycliBarrier是各个线程执行到某位置之后,然后所有线程一齐开始后续的工作。相同的是两者都属于线程计数器。

    使用示例如下: boss等待所有员工来开会,当所有人员都到齐之后,boss宣布开始会议!!!

    7、Semaphore 信号量

    Semaphore在线程协作方面主要用于控制同时访问临界区资源的线程个数。信号量是属于操作系统层面的概念,jdk提供了操作接口。

    根据结果可以看出只有当有线程释放资源之后,才会有新的线程获取到资源。即控制了同一时间访问临界区资源的线程数量。当Semaphore(1)设置为1的时候,此时可以当做锁来使用。

    8、Socket套接字

    利用Java的socket套接字的方式来实现,也就是常用的tcp和udp这些协议的封装,这种一般用于分布式系统,单机环境下线程通迅用这个就有点浪费和复杂了。

    多线程通信带来的问题

    多个线程同一时刻对同一份资源进行操作时,如果跟我们预期的结果不一样,就会产生线程不安全的问题。有可能出现多个线程先后更改数据造成所得到的数据是脏数据。
    同时线程安全的问题只会出现在多线程环境中。
    举个例子:现在有两个线程分别是线程1和线程2,他们同时对一个共享变量count直接进行多次count++的操作,我们知道cout++的操作是分为三个步骤的:

    1. 读:线程读取count的值到自己的工作内存
    2. 改:线程在工作内存中对count进行加一的操作
    3. 写:线程将计算结果写入共享变量所在内存

    在这里插入图片描述
    上图一看并没有什么问题,但如果在线程1进行读后,假如他现在看到的count值为6,此时线程2也开始读,它看到的也是6,随后线程2继续进行改和写的操作,此时共享内存中的count值变为了7,但是线程1还是在对6这个值进行操作,这就产生了线程安全的问题。
    在这里插入图片描述

    多线程通信导致线程不安全的原因

    • 线程对共享变量的所有操作都在自己的工作内存中进行,不是直接从主内存中读写
    • 不同线程之间无法直接访问其他线程工作内存中的变量,线程间变量的传递主要通过主内存来完成
    • 多线程下的代码执行顺序是不确定的

    怎样解决线程不安全

    对多线程下共享的资源加锁。使用 synchronized 包围对应代码块,保证多线程之间是互斥的。每次只能有一个线程访问其共享资源。如多个线程在执行同一段代码的时候采用加锁机制,使每次的执行结果和单线程执行的结果都是一样的,而且其他的变量的值也和预期的是一样的,不存在执行程序时出现意外结果。

    展开全文
  • //线程之间通过管道传递数据 public class Test { public static void main(String[] args) throws IOException { PipedInputStream input = new PipedInputStream(); PipedOutputStream out = new ...
  • 多线程通讯管道

    2017-11-16 21:27:00
    管道流是JAVA线程通讯的常用方式之一,基本流程如下: 1)创建管道输出流PipedOutputStream pos和管道输入流PipedInputStream pis 2)将pos和pis匹配,pos.connect(pis); 3)将pos赋给信息输入线程,pis赋给...

    管道流是JAVA中线程通讯的常用方式之一,基本流程如下:

    1)创建管道输出流PipedOutputStream pos和管道输入流PipedInputStream pis

    2)将pos和pis匹配,pos.connect(pis);

    3)将pos赋给信息输入线程,pis赋给信息获取线程,就可以实现线程间的通讯了

     

    import java.io.IOException;  
    import java.io.PipedInputStream;  
    import java.io.PipedOutputStream;  
      
    public class testPipeConnection {  
      
        public static void main(String[] args) {  
            /** 
             * 创建管道输出流 
             */  
            PipedOutputStream pos = new PipedOutputStream();  
            /** 
             * 创建管道输入流 
             */  
            PipedInputStream pis = new PipedInputStream();  
            try {  
                /** 
                 * 将管道输入流与输出流连接 此过程也可通过重载的构造函数来实现 
                 */  
                pos.connect(pis);  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
            /** 
             * 创建生产者线程 
             */  
            Producer p = new Producer(pos);  
            /** 
             * 创建消费者线程 
             */  
            Consumer1 c1 = new Consumer1(pis);  
            /** 
             * 启动线程 
             */  
            p.start();  
            c1.start();  
        }  
    }  
      
    /** 
     * 生产者线程(与一个管道输入流相关联) 
     *  
     */  
    class Producer extends Thread {  
        private PipedOutputStream pos;  
      
        public Producer(PipedOutputStream pos) {  
            this.pos = pos;  
        }  
      
        public void run() {  
            int i = 0;  
            try {  
                while(true)  
                {  
                this.sleep(3000);  
                pos.write(i);  
                i++;  
                }  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        }  
    }  
      
    /** 
     * 消费者线程(与一个管道输入流相关联) 
     *  
     */  
    class Consumer1 extends Thread {  
        private PipedInputStream pis;  
      
        public Consumer1(PipedInputStream pis) {  
            this.pis = pis;  
        }  
      
        public void run() {  
            try {  
                while(true)  
                {  
                System.out.println("consumer1:"+pis.read());  
                }  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
    }  

    程序启动后,就可以看到producer线程往consumer1线程发送数据

     

    管道流虽然使用起来方便,但是也有一些缺点

    1)管道流只能在两个线程之间传递数据

    线程consumer1和consumer2同时从pis中read数据,当线程producer往管道流中写入一段数据后,每一个时刻只有一个线程能获取到数据,并不是两个线程都能获取到producer发送来的数据,因此一个管道流只能用于两个线程间的通讯。不仅仅是管道流,其他IO方式都是一对一传输。

    2)管道流只能实现单向发送,如果要两个线程之间互通讯,则需要两个管道流

     可以看到上面的例子中,线程producer通过管道流向线程consumer发送数据,如果线程consumer想给线程producer发送数据,则需要新建另一个管道流pos1和pis1,将pos1赋给consumer1,将pis1赋给producer,具体例子本文不再多说。

    转载于:https://www.cnblogs.com/fengyan20150508/p/7846563.html

    展开全文
  • Java 多线程同步

    2019-06-25 15:24:00
     本文主要描述,Java多线程共享数据(同步)/线程死锁/生产者与消费者应用案例。  多线程的优势:资源利用率好、程序设计在某种情况下更简单、程序响应更快。  进程和线程之间如何通信:进程间通讯依靠 IPC ...

    引言

      本文主要描述,Java中多线程共享数据(同步)/线程死锁/生产者与消费者应用案例。

      多线程的优势:资源利用率好、程序设计在某种情况下更简单、程序响应更快。

      进程和线程之间如何通信:进程间通讯依靠 IPC 资源,例如管道(pipes)、套接字(sockets)等;线程间通讯依靠 JVM 提供的 API,例如 wait()、notify()、notifyAll() 等方法,线程间还可以通过共享的主内存来进行值的传递。

    多线程共享数据(同步)

      线程同步:多个线程在同一个时间段只能有一个线程执行其指定代码,其他线程要等待此线程完成之后才可以继续执行。

      多线程共享数据的安全问题,使用同步解决。

      创建一个多线程:同步方式按照如下所示

    public static void main(String[] args) {
            MyThread s0 = new MyThread();
            Thread t1 = new Thread(s0,"one");
            Thread t2 = new Thread(s0,"two");
            t1.start();
            t2.start();
        }

    同步代码块

      使用方式:synchronized(要同步的对象){ 要同步的操作 }

    class MyThread implements Runnable{
        Object obj = new Object(); //同步的标记对象
        @Override
        public void run() {
            //同步代码块
            synchronized(obj){
                System.out.println(Thread.currentThread().getName()+" is doing...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" finished.");
            }
        }
    }
    

    同步方法

      使用方式:public synchronized void method(){ 要同步的操作 },同步的是当前对象(this)

    public synchronized void doMethod(){
            System.out.println(Thread.currentThread().getName()+" is doing...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" finished.");
        }
    

    线程死锁

      当一个对象内,同步过多,会造成斯死锁。

      定义一个服务员类:Waiter,其下有两个同步方法

    public class Waiter {
    
        public synchronized void say(Customer c) {
            System.out.println("Waiter: Pay --> Do");
            c.doService();
        }
    
        public synchronized void doService() {
            System.out.println("Waiter: OK");
        }
    }
    

      定义一个消费者类:Customer,其下有两个同步方法

    public class Customer {
        public synchronized void say(Waiter w) {
            System.out.println("Customer: Do --> Pay");
            w.doService();
        }
    
        public synchronized void doService() {
            System.out.println("Customer: OK");
        }
    }
    

      当同时访问的时候,就会造成数据死锁。

    public class Main {
        public static void main(String[] args) {
            new MyThred();
        }
    }
    
    class MyThred implements Runnable {
        Customer c = new Customer();
        Waiter w = new Waiter();
    
        public MyThred() {
            new Thread(this).start();
            w.say(c);
        }
    
        @Override
        public void run() {
            c.say(w);
        }
    }
    

      总结:当 synchronized 用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

    生产者与消费者应用案例

      案例需求:生产者不断生成产品,消费者不断取走产品。

    创建共享资源类

    package lib;
    
    /**
     * 共享资源类
     */
    public class Storage {
        private String type;
        private String date;
    
        private boolean isEmpty = true;
    
        /**
         * 生产者向共享资源存储数据
         * @param type
         * @param date
         */
        public synchronized void push(String type, String date) {
            try {
                while (!isEmpty) {
                    // 当前共享资源状态不为空,等待消费者消费
                    this.wait();
                }
                // 生产开始
                this.type = type;
                Thread.sleep(10);
                this.date = date;
                // 生产结束
                isEmpty = false;
                this.notifyAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 消费者从共享资源中取出数据,并打印
         */
        public synchronized void popup() {
            try {
                while (isEmpty) {
                    //当资源为空,释放同步锁,进入等待
                    this.wait();
                }
                Thread.sleep(10);
                //消费开始
                System.out.println(this.type + "-->" + this.date);
                //消费结束
                isEmpty = true;
                this.notifyAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    }
    View Code

    创建生产者类

    package lib;
    
    /**
     * 生产者
     */
    public class Producer implements Runnable {
        private Storage storage;
    
        public Producer(Storage storage) {
            this.storage = storage;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                if (i%2==0) {
                    storage.push("apple", "20190625001");
                }
                else {
                    storage.push("banana", "20190625002");
                }
            }
        }
    }

    创建消费者类

    package lib;
    
    public class Consumer implements Runnable {
        private Storage storage;
    
        public Consumer(Storage storage) {
            this.storage = storage;
        }
    
        @Override
        public void run() {
            for (int i=0;i<20;i++) {
                storage.popup();
            }
        }
    }
    

    执行测试:两个生产者,两个消费者

    package lib;
    
    public class Test {
    
        public static void main(String[] args) {
            Storage storage = new Storage();
    
            Producer p1 = new Producer(storage);
            Producer p2 = new Producer(storage);
    
            Consumer c1 = new Consumer(storage);
            Consumer c2 = new Consumer(storage);
    
            Thread t1 = new Thread(p1, "P1");
            Thread t2 = new Thread(p2, "P2");
            Thread t3 = new Thread(c1, "C1");
            Thread t4 = new Thread(c2, "C2");
    
            t1.start();
            t2.start();
            t3.start();
            t4.start();
    
        }
    }
    

      总结说明: 

        wait():执行该方法的线程对象释放同步锁,JVM把该线程存放到等待池中,等待其他的线程唤醒该线程。
        notify:执行该方法的线程唤醒在等待池中等待的任意一个线程,把线程转到锁池中等待。
        notifyAll():执行该方法的线程唤醒在等待池中等待的所有的线程,把线程转到锁池中等待。

    使用lock

      wait和notify方法,只能被同步监听锁对象来调用,否则报错IllegalMonitorStateException。那么现在问题来了,Lock机制根本就没有同步锁了,也就没有自动获取锁和自动释放锁的概念。因为没有同步锁,所以Lock机制不能调用wait和notify方法。解决方案:Java5中提供了Lock机制的同时提供了处理Lock机制的通信控制的Condition接口。
    从Java5开始,可以:
          1):使用Lock机制取代synchronized 代码块和synchronized 方法。
          2):使用Condition接口对象的await,signal,signalAll方法取代Object类中的wait,notify,notifyAll方法。

    package lib;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 共享资源类
     */
    public class Storage {
        private String type;
        private String date;
    
        private boolean isEmpty = true;
    
        private final Lock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();
    
        /**
         * 生产者向共享资源存储数据
         * @param type
         * @param date
         */
        public void push(String type, String date) {
            lock.lock(); //获取对象锁
            try {
                while (!isEmpty) {
                    // 当前共享资源状态不为空,等待消费者消费
                    condition.await();
                }
                // 生产开始
                this.type = type;
                Thread.sleep(10);
                this.date = date;
                // 生产结束
                isEmpty = false;
                condition.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            finally {
                lock.unlock(); //释放对象锁
            }
        }
    
        /**
         * 消费者从共享资源中取出数据,并打印
         */
        public void popup() {
            lock.lock();
            try {
                while (isEmpty) {
                    //当资源为空,释放同步锁,进入等待
                    condition.await();
                }
                Thread.sleep(10);
                //消费开始
                System.out.println(this.type + "-->" + this.date);
                //消费结束
                isEmpty = true;
                condition.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            finally {
                lock.unlock();
            }
        }
    
    }
    View Code

     

    转载于:https://www.cnblogs.com/huanghzm/p/11078717.html

    展开全文
  • 多线程-线程通讯-生产者消费者模型 多线程的使用往往不单单是单个线程的实现,而是多个线程的相互协作实现逻辑业务。 什么是线程通讯? 多个线程协作完成共享资源的使用,实现线程安全,线程不死锁。 线程通讯实现...
  • Java多线程学习之二

    2020-12-17 23:50:33
    每个进程之间是独立的,除非利用某些通讯管道来进行通信,或是通过操作系统产生交互作用,否则基本上各进程不知道彼此的存在。 3、任务(Multi task)是指在一个系统中可以运行个进程,即有个独立的运行的任务...
  • 1 多线程之间如果想要通讯,可以通过管道流来实现,例如两个子线程之间想要互通数据,可以通过管道流(PipedOutputStream和PipedInputStream)来实现数据互通,PipedOutputStream负责write,PipedInputStream负责...
  • Java对于多线程之间的通信提供了一个机制,管道,虽然说线程之间完全可以通过共享变量进行通信,但是java提供了这么一个机制也是应该了解下的,毕竟专门提供的机制肯定有它的好处。由于相对简单(估计内部其实也是用...
  • Java 提供了四个相关的管道流,我们可以使用其在多线程进行数据传递,其分别是 类名 作用 备注 PipedInputStream 字节管道输入流 字节流 PipedOutputStream 字节管道输出流 字节流 PipedReader 字符管道...
  • java中PipedStream管道流通信详细使用(详解)

    万次阅读 多人点赞 2019-08-04 10:23:33
    Java 提供了四个相关的管道流,我们可以使用其在多线程进行数据传递,其分别是 类名 作用 备注 PipedInputStream 字节管道输入流 字节流 PipedOutputStream 字节管道输出流 字节流 PipedReader 字符管道读取 字符流 ...
  • 多线程

    2019-03-22 09:02:33
    线程通讯 java.long.Object wait() 释放了所持有的监视器对象上的锁,导致当前的线程等待 notify() 随机唤醒等待的线程 notifyAll() 唤醒正在等待一个给定对象的所有线程 ...
  • 它们的作用是让多线程可以通过管道进行线程间的通讯。在使用管道通信时,必须将PipedOutputStreamPipedInputStream配套使用。使用管道通信时,大致的流程是:在线程A中写入数据,这些数据会存储在PipedInputStream的...
  • 它们的作用是[b]让多线程可以通过管道进行线程间的通讯[/b]。在使用管道通信时,必须将PipedOutputStream和PipedInputStream配套使用。 使用管道通信时,大致的流程是:我们在线程A中向PipedOutputStrea...
  • JAVA中进程、线程

    2017-09-04 21:29:52
    1.一个进程可以有线程,一个线程只能属于一个进程 2.系统分配资源给进程,同一进程的线程共享该进程的资源 3.进程间可以并发执行,同一进程的线程也可以并发执行 进程的通讯方法 1.管道:具有亲缘关系...
  • java中PipedInputStream和PipedOutputStream分别是管道输入流和管道输出流,它的作用是让多线程可以通过管道进行线程间的通讯,在使用管道通信时,必须将PipedInputStream和PipedOutputStream配套使用。大致的流程是...
  • 管道流(线程通信流):管道流的主要作用是可以进行两个线程间的通讯,分为管道输出流(PipedOutputStream)、管道输入流(PipedInputStream),如果想要...使用多线程操作,结合线程进行操作。通常由某个线程从管道...
  • 线程流必须输入输出一起使用只使用一个会抛出 java.io.IOException: Pipe not connected而且也不能一个对应个 只能一对一,一对会抛出java.io.IOException: Already connected; 例: import java.io....
  • 在进行多线程编程时,经常要使用同步互斥机构,但Java本身没有提供的同步互斥机构,仅提供了两个与同步互斥有关的方法:wait()和notify(),可以用来设计信号量类:mySemaphore,它是按照Dijkstra提出的计数信号量的...
  • 他们的作用是让多线程可以通过管道进行线程间的通讯,在使用管道通信时,必须将PipedOutputStream和PipedInputStream配套使用 使用管道通信时,大致的流程是:我们在线程A中向PipedOutputStream中写入数据,这些...
  • (2)命名管道:半双工的通讯方式,可以在非亲缘关系的进程间通信; (3)信号:用于通知进程有某种事件发生; (4)信号量:他是一个计数器,可以控制个进程共享资源的访问。常被作为锁来使用。防止某进程正在...
  • 这篇我们讲多线程间接协作的方式,阻塞队列和管道通讯,间接协作的优点是使用起来更简单并且不易出错。 阻塞队列 阻塞队列提供了一种功能,即你可以在任何时刻向队列内扔一个对象,如果队列满了则当前...
  • 本章,我们对java 管道进行学习。 ... java 管道介绍 在java中,PipedOutputStream和PipedInputStream分别是管道输出流...它们的作用是让多线程可以通过管道进行线程间的通讯。在使用管道通信时,必须将PipedOutputSt...
  • 他的作用是让多线程可以通过管道进行线程间的通信,在使用管道通讯时候,必须将两者配套使用。使用管道的大致流程是:我们在进程A中向PipedOutputStream中写入数据,然后这些数据会自动发送到与PipedOutputStream...
  • 线程通信

    2018-05-28 15:03:34
    管道 线程之间的通讯问题是指线程之间相互传递信息,这些信息包括数据、控制指令等。我们前面举例中数据共享也是线程的一种通信方式。此外,java语言还提供了线程之间通过管道来进行通信的方式。其结构表示。管道...
  • 线程通常将非阻塞 IO 的空闲时间用于在其他通道上执行 IO 操作,所以单独的线程可以管理个输入和输出通道。因此,NIO 可以让服务器端使用一个或有限几个线程来同时处理连接到服务器端的所有客户端。 在使用的时候...
  • 如果线程避免阻塞和事务操作(例如BLPOP和MULTI / EXEC则可以共享一个连接。 生菜是用建造的。 支持高级Redis功能,例如Sentinel,群集,管道,自动重新连接和Redis数据模型。 此版本的Lettuce已针对最新的...

空空如也

空空如也

1 2 3
收藏数 48
精华内容 19
关键字:

java多线程管道通讯

java 订阅