Java线程间通信

来源:互联网 发布:威锋软件源地址 编辑:程序博客网 时间:2024/06/05 05:21

管道流

管道流分为管道输入流PipedInputStream和管道输出流PipedOutputStream,两者必须联合使用管道输入流内部有一个循环缓冲字节数组(以下称缓冲数组),默认大小是1024。管道输入流读取其缓冲数组的数据,管道输出流实际上是通过调用管道输入流的方法往缓冲数组写数据。当缓冲数组满了,管道输出流所在线程被阻塞。当缓冲数组为空,管道输入流所在线程被阻塞。不建议对这两个对象尝试使用单个线程,因为如果读或者写被阻塞,那么就会造成死锁。

管道输入流和管道输出流都有connect方法,这个方法将两个流连接起来,只要调用任意一个就可以,否则会报异常IOException("Already connected")。管道输出流的connect方法实际上是调用了管道输入流的connect方法。如果管道输出流所在的线程已经终止,那么管道输入流再去读取的话,就会报错。

    public static void main(String[] args) throws Exception {        final PipedInputStream pipedInputStream = new PipedInputStream();        final PipedOutputStream pipedOutputStream = new PipedOutputStream();        pipedInputStream.connect(pipedOutputStream);        Thread inThread = new Thread() {            @Override            public void run() {                try {                    while (true) {                        System.out.println("开始消费");                        System.out.println("返回:" + pipedInputStream.read());                    }                } catch (Exception e) {                    e.printStackTrace();                }            }        };        Thread outThread = new Thread() {            @Override            public void run() {                try {                    for (int i = 0; i < 3; i++) {                        Thread.sleep(1000);                        System.out.println("开始生产");                        pipedOutputStream.write(new Random().nextInt(128));                    }                } catch (Exception e) {                    e.printStackTrace();                }            }        };        inThread.start();        outThread.start();    }

输出
开始消费开始生产开始生产返回:45开始消费返回:23开始消费开始生产返回:6开始消费java.io.IOException: Write end deadat java.io.PipedInputStream.read(PipedInputStream.java:311)at Demo$1.run(Demo.java:23)

对象内部锁和内部条件

import java.util.Random;public class Demo {    private class Production {        /**         * 下一个生产的产品的索引         */        private int in = 0;        /**         * 下一个消费的产品索引         */        private int out = -1;        /**         * 产品数组         */        private int[] ints = new int[1024];        /**         * 生产         *         * @param i         */        public synchronized void produce(int i) {            while (in == ints.length) {                try {                    this.wait();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            ints[in] = i;            out = in;            in++;            this.notifyAll();        }        /**         * 消费         *         * @return         */        public synchronized int comsume() {            while (out < 0) {                try {                    this.wait();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            int i = ints[out];            in = out;            out--;            this.notifyAll();            return i;        }    }    /**     * @param args     */    public static void main(String[] args) {        final Production production = new Demo().new Production();        Thread comsumeThread = new Thread() {            @Override            public void run() {                while (true) {                    System.out.println("开始消费");                    System.out.println("返回:" + production.comsume());                }            }        };        Thread produceThread = new Thread() {            @Override            public void run() {                for (int i = 0; i < 10; i++) {                    try {                        Thread.sleep(1000);                        System.out.println("开始生产");                        production.produce(new Random().nextInt(128));                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        };        comsumeThread.start();        produceThread.start();    }}


输出
开始消费开始生产返回:110开始消费开始生产返回:11开始消费开始生产返回:73开始消费开始生产返回:18开始消费开始生产返回:43开始消费开始生产返回:100开始消费开始生产返回:47开始消费开始生产返回:43开始消费开始生产返回:101开始消费开始生产返回:62开始消费(这里阻塞了)

锁和条件

import java.util.Random;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;public class Demo {    private class Production {        /**         * 锁         */        private ReentrantLock reentrantLock = new ReentrantLock();        /**         * 条件对象         */        private Condition condition = reentrantLock.newCondition();        /**         * 下一个生产的产品的索引         */        private int in = 0;        /**         * 下一个消费的产品索引         */        private int out = -1;        /**         * 产品数组         */        private int[] ints = new int[1024];        /**         * 生产         *         * @param i         */        public void produce(int i) {            reentrantLock.lock();            try {                while (in == ints.length) {                    try {                        condition.await();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }                ints[in] = i;                out = in;                in++;                condition.signalAll();            } finally {                reentrantLock.unlock();            }        }        /**         * 消费         *         * @return         */        public int comsume() {            reentrantLock.lock();            try {                while (out < 0) {                    try {                        condition.await();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }                int i = ints[out];                in = out;                out--;                condition.signalAll();                return i;            } finally {                reentrantLock.unlock();            }        }    }    /**     * @param args     */    public static void main(String[] args) {        final Production production = new Demo().new Production();        Thread comsumeThread = new Thread() {            @Override            public void run() {                while (true) {                    System.out.println("开始消费");                    System.out.println("返回:" + production.comsume());                }            }        };        Thread produceThread = new Thread() {            @Override            public void run() {                for (int i = 0; i < 10; i++) {                    try {                        Thread.sleep(1000);                        System.out.println("开始生产");                        production.produce(new Random().nextInt(128));                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        };        comsumeThread.start();        produceThread.start();    }}

输出内容与对象内部锁和内部条件的例子一样。

阻塞队列

import java.util.Random;import java.util.concurrent.ArrayBlockingQueue;public class Demo {    /**     * @param args     */    public static void main(String[] args) {        final ArrayBlockingQueue<Integer> integers = new ArrayBlockingQueue<Integer>(1024);        Thread comsumeThread = new Thread() {            @Override            public void run() {                while (true) {                    try {                        System.out.println("开始消费");                        System.out.println("返回:" + integers.take());                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        };        Thread produceThread = new Thread() {            @Override            public void run() {                for (int i = 0; i < 10; i++) {                    try {                        Thread.sleep(1000);                        System.out.println("开始生产");                        integers.put(new Random().nextInt(128));                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        };        comsumeThread.start();        produceThread.start();    }}

输出内容与对象内部锁和内部条件的例子一样。

如何终止消费者线程?

import java.util.Random;import java.util.concurrent.ArrayBlockingQueue;public class Demo {    /**     * @param args     */    public static void main(String[] args) {        final ArrayBlockingQueue<Integer> integers = new ArrayBlockingQueue<Integer>(1024);        class ConsumeThread extends Thread {            public ConsumeThread(ThreadGroup threadGroup, String name) {                super(threadGroup, name);            }            @Override            public void run() {                while (!this.isInterrupted()) {                    try {                        System.out.println(Thread.currentThread().getName() + "返回:" + integers.take());                    } catch (InterruptedException e) {                        e.printStackTrace();                        this.interrupt();                    }                }            }        }        final ThreadGroup threadGroup = new ThreadGroup("comsumeThreadGroup");        Thread comsumeThread1 = new ConsumeThread(threadGroup, "comsumeThread1");        Thread comsumeThread2 = new ConsumeThread(threadGroup, "comsumeThread2");        Thread produceThread = new Thread() {            @Override            public void run() {                for (int i = 0; i < 10; i++) {                    try {                        Thread.sleep(1000);                        System.out.println("开始生产");                        integers.put(new Random().nextInt(128));                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }while (integers.size() != 0) {                }                threadGroup.interrupt();            }        };        comsumeThread1.start();        comsumeThread2.start();        produceThread.start();    }}

输出
开始生产comsumeThread2返回:57开始生产comsumeThread1返回:74开始生产comsumeThread2返回:97开始生产comsumeThread1返回:34开始生产comsumeThread2返回:99开始生产comsumeThread1返回:8开始生产comsumeThread2返回:6开始生产comsumeThread1返回:107开始生产comsumeThread2返回:45开始生产comsumeThread1返回:52java.lang.InterruptedExceptionat java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2017)at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2052)at java.util.concurrent.ArrayBlockingQueue.take(ArrayBlockingQueue.java:374)at Demo$1ConsumeThread.run(Demo.java:27)java.lang.InterruptedExceptionat java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2017)at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2052)at java.util.concurrent.ArrayBlockingQueue.take(ArrayBlockingQueue.java:374)at Demo$1ConsumeThread.run(Demo.java:27)
注意,捕获到InterruptedException时,要继续设置中断状态this.interrupt(),因为阻塞队列内部是使用可重入锁和条件的,当wait、await、sleep等方法阻塞过程中被中断时会抛出InterruptedException并且清除中断状态,所以要么往外抛出InterruptedException,要么在catch里继续设置中断状态而不能什么都不做。

原创粉丝点击