Java线程间通信

管道流

管道流分为管道输入流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 dead
	at 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 integers = new ArrayBlockingQueue(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 integers = new ArrayBlockingQueue(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返回:52
java.lang.InterruptedException
	at 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.InterruptedException
	at 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里继续设置中断状态而不能什么都不做。

你可能感兴趣的:(Java,SE)