Java 线程间通信

要实现多个线程之间的协同,需要涉及到线程之间相互通信,线程间通信分为以下四类:

  1. 文件共享
  2. 网络共享
  3. 共享全局变量
  4. jdk提供的线程协调API

本文只讲解jdk提供的API。
三种线程协作通信的方式:

  • suspend/resume(已弃用)
  • wait/notify
  • park/unpark

suspend/resume

示例(生产者—消费者模型):线程1买包子,发现没有包子,停止执行,线程2生产出包子,通知线程1继续执行。

public class Test {
    /** 包子 */
    public static Object baozi = null;

    public static void main(String[] args) throws Exception {
        suspendResumeTest();
    }

    /** 正常的suspend/resume */
    public static void suspendResumeTest() throws Exception {
        // 启动线程
        Thread consumerThread = new Thread(() -> {
            while (baozi == null) { // 如果没包子,则进入等待
                System.out.println("没有包子,进入等待");
                Thread.currentThread().suspend();
            }
            System.out.println("买到包子");
        });
        consumerThread.start();
        // 1秒之后,生产一个包子
        Thread.sleep(1000L);
        baozi = new Object();
        consumerThread.resume();
        System.out.println("生产出包子,通知消费者买包子");
    }
}

在这里插入图片描述
正常消费情况下没有出现问题,但是使用suspend/resume 容易出现死锁情况。


死锁情况1:
当消费者线程(consumerThread)中存在同步代码块时,suspend并不会释放锁。而其他线程由于抢不到锁会一直处于阻塞状态。也就一直无法通知消费者线程继续执行。

public class Test {

    /** 包子 */
    public static Object baozi = null;

    public static void main(String[] args) throws Exception {

        new Test().suspendResumeDeadLockTest();
    }
	public void suspendResumeDeadLockTest() throws Exception {
		// 启动线程
		Thread consumerThread = new Thread(() -> {
			while (baozi == null) { // 如果没包子,则进入等待
				System.out.println("没包子,进入等待");
				// 当前线程拿到锁,然后挂起
				synchronized (this) {
					Thread.currentThread().suspend();
				}
			}
			System.out.println("买到包子");
		});
		consumerThread.start();
		// 1秒之后,生产一个包子
		Thread.sleep(1000L);
		baozi = new Object();
		// 争取到锁以后,再恢复consumerThread
		synchronized (this) {
			consumerThread.resume();
		}
		System.out.println("生产出包子,通知消费者买包子");
	}
}

在这里插入图片描述

死锁情况2:
resume先执行,suspend后执行也会导致程序永久挂起。

public class Test {

    /** 包子 */
    public static Object baozi = null;

    public static void main(String[] args) throws Exception {

        new Test().suspendResumeDeadLockTest2();
    }
	public void suspendResumeDeadLockTest2() throws Exception {
		// 启动线程
		Thread consumerThread = new Thread(() -> {
			while (baozi == null) {
				System.out.println("没包子,进入等待");
				try { // 三秒后挂起
					Thread.sleep(3000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				// 这里的挂起执行在resume后面
				Thread.currentThread().suspend();
			}
			System.out.println("买到包子");
		});
		consumerThread.start();
		// 1秒之后,生产一个包子
		Thread.sleep(1000L);
		baozi = new Object();
		consumerThread.resume();
		System.out.println("生产出包子,通知消费者买包子");
		consumerThread.join();
	}
}

在这里插入图片描述

wait/notify机制

wait方法会导致当前线程等待,加入该对象的等待集合中,并且放弃当前持有的对象锁,notify/notifyAll方法唤醒一个或所有正在等待这个对象锁的线程。

注意:
虽然wait会自动解锁,但是对顺序有要求,如果notify在wait调用前执行,那么线程也会永远处于等待状态。

public class Test {

    /** 包子 */
    public static Object baozi = null;

    public static void main(String[] args) throws Exception {
        new Test().waitNotifyTest();
    }
	/** 正常的wait/notify */
    public void waitNotifyTest() throws Exception {
        // 启动线程
        new Thread(() -> {
            while (baozi == null) { // 如果没包子,则进入等待
                synchronized (this) {
                    try {
                        System.out.println("没有包子,进入等待\"");
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            System.out.println("买到包子");
        }).start();
        // 1秒之后,生产一个包子
        Thread.sleep(1000L);
        baozi = new Object();
        synchronized (this) {
            this.notifyAll();
            System.out.println("生产出包子,通知消费者买包子");
        }
    }
}

在这里插入图片描述

park/unpark机制

线程调用park等待“许可”,unpark方法为指定线程提供“许可”。
不要求park/unpark方法的调用顺序,多次调用unpark不会叠加,之后第一次调用park,线程会直接运行,后续调用则会进入等待。
注意:
unpark不会主动释放锁

import java.util.concurrent.locks.LockSupport;

public class Test {

    /** 包子 */
    public static Object baozi = null;

    public static void main(String[] args) throws Exception {

        new Test().parkUnparkTest();
    }
	/** 正常的park/unpark */
    public void parkUnparkTest() throws Exception {
        // 启动线程
        Thread consumerThread = new Thread(() -> {
            while (baozi == null) { 
                try {
                    //等待三秒
                    Thread.sleep(3000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("进入等待");
                LockSupport.park();
            }
            System.out.println("买到包子");
        });
        consumerThread.start();
        // 1秒之后,生产一个包子
        Thread.sleep(1000L);
        baozi = new Object();
        LockSupport.unpark(consumerThread);
        System.out.println("生产出包子,通知消费者买包子");
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值