Java线程通信有几种方式

前言

多线程并发编程时,难免会遇到线程间的通信问题。线程通信方式的思想大体上来说可以分为两种:共享传递

线程通信

共享的实现方式可以是共享变量、共享文件、数据库、网络等。传递的实现方式可以是消息队列、生产者-消费者模型等,并且这两种通信方式也不是绝对独立的,比如生产者-消费者模型也可以看成是共享内存区域。
对于共享方式,最简单的方式莫过于共享变量。有这样一道题:

使用两个线程,交替打印奇数和偶数?

这道题的主要考点就是:线程间如何通信?一个线程打印一次后,如何通知另外一个线程打印,以此循环,最终效果就是交替打印。

共享变量

利用共享变量,可以这样来实现

public class ThreadDemo {

    // 共享变量,线程运行标识
    private static volatile Boolean odd = true;
    private static int number = 1;

    public static void main(String[] args) throws Exception {
        // 奇数
        Thread threadOdd = new Thread(() -> {
            while (number <= 10) {
                while (odd) {
                    System.out.println(Thread.currentThread().getName() + ":" + number);
                    number++;
                    odd = false;
                }
            }
        });

        // 偶数
        Thread threadEven = new Thread(() -> {
            while (number <= 10) {
                while (!odd) {
                    System.out.println(Thread.currentThread().getName() + ":" + number);
                    number++;
                    odd = true;
                }
            }
        });

        threadOdd.start();
        threadEven.start();
    }
}

输出结果

Thread-0:1
Thread-1:2
Thread-0:3
Thread-1:4
Thread-0:5
Thread-1:6
Thread-0:7
Thread-1:8
Thread-0:9
Thread-1:10

wait/notify

如果使用wait/notify机制实现,代码如下:

public class ThreadDemo {

    private static int number = 1;
    private static final Object object = new Object();

    public static void main(String[] args) throws Exception {
        // 奇数
        Thread threadOdd = new Thread(() -> {
            synchronized (object) {
                while (number <= 10) {
                    System.out.println(Thread.currentThread().getName() + ":" + number);
                    number++;
                    try {
                        object.notify();
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 终止程序
                object.notify();
            }
        });

        // 偶数
        Thread threadEven = new Thread(() -> {
            synchronized (object) {
                while (number <= 10) {
                    System.out.println(Thread.currentThread().getName() + ":" + number);
                    number++;
                    try {
                        object.notify();
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 终止程序
                object.notify();
            }
        });

        threadOdd.start();
        threadEven.start();
    }
}

运行结果如下:

Thread-0:1
Thread-1:2
Thread-0:3
Thread-1:4
Thread-0:5
Thread-1:6
Thread-0:7
Thread-1:8
Thread-0:9
Thread-1:10

wait/notify改进

前面两种方法虽然运行结果正确,但是不能保证奇数线程一定先运行。
要想保证奇数线程先运行,可以对上述代码稍微改进一下,以wait/notify为例:

/**
 * @author sicimike
 */
public class ThreadDemo {

    private static int number = 1;
    private static final Object object = new Object();
    final static CountDownLatch latch = new CountDownLatch(1);

    public static void main(String[] args) {
        // 奇数线程
        Thread threadOdd = new Thread(() -> {
            synchronized (object) {
                latch.countDown();
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.currentThread().getName() + " : " + number++);
                    try {
                        object.notify();
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                object.notify();
            }
        });

        // 偶数线程
        Thread threadEven = new Thread(() -> {
            try {
                latch.await();
                synchronized (object) {
                    for (int i = 0; i < 5; i++) {
                        System.out.println(Thread.currentThread().getName() + " : " + number++);
                        object.notify();
                        object.wait();
                    }
                    object.notify();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        threadOdd.start();
        threadEven.start();
    }
}

借助CountDownLatch类可以轻易实现奇数线程先运行。

LockSupport工具

LockSupport工具类在 suspend/resume、wait/notify、park/unpark 已经详细讲解过,此处不再赘述。

这个例子中,也是可以保证奇数线程先执行的

/**
 * @author sicimike
 */
public class ThreadDemo {

    private static int number = 1;
    private static Thread threadOdd = null;
    private static Thread threadEven = null;

    public static void main(String[] args) {
        // 奇数线程
        threadOdd = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + " : " + number++);
                // 唤醒threadEven线程
                LockSupport.unpark(threadEven);
                // 阻塞当前线程
                LockSupport.park();
            }
        });

        // 偶数线程
        threadEven = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
            	// 阻塞threadEven,使threadOdd先运行
                LockSupport.park();
                System.out.println(Thread.currentThread().getName() + " : " + number++);
                // 唤醒threadOdd线程
                LockSupport.unpark(threadOdd);
            }
        });

        threadOdd.start();
        threadEven.start();
    }
}

执行结果

Thread-0 : 1
Thread-1 : 2
Thread-0 : 3
Thread-1 : 4
Thread-0 : 5
Thread-1 : 6
Thread-0 : 7
Thread-1 : 8
Thread-0 : 9
Thread-1 : 10

结语

有时候程序结果是正确的,不代表程序就是完全正确的,特别是涉及到多线程的代码。如上述代码有错误之处,还请不吝赐教。

发布了52 篇原创文章 · 获赞 107 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Baisitao_/article/details/100062949