Java线程通信的四种形式及代码示例

线程通信是指多个线程之间的协同,如:线程执行先后顺序、获取某个线程的执行结果等。涉及到线程之间相互通信,分为四类:文件共享、网络共享、变量共享、JDK提供的线程协调API。

线程协调API

通过多线程协助的典型场景:生产者-消费者模型,来说明。

1、被弃用的suspend/resume

由于suspend()执行后不会释放锁,导致resume()永远无法拿到锁,而无法通知线程继续执行,示例代码:

	public void suspendResumeDeadLockTest() throws Exception{
		baozi = null;
		Thread consumerThread = new Thread(() -> {
			while(baozi == null){
				System.out.println("没包子,进入等待...");
				synchronized (this) {
					Thread.currentThread().suspend();
				}
			}
			System.out.println("买到包子,回家...");
		});
		//	开始线程
		consumerThread.start();
		//	3秒后,生产包子
		Thread.sleep(3000);
		baozi = new Object();
		synchronized (this) {
			consumerThread.resume();
		}
		System.out.println("通知消费者");
	}

输出

没包子,进入等待...

由于suspend/resume对执行顺序有强制的先执行suspend()后执行resume(),否则线程无法正常执行,示例代码:

	public void suspendResumeDeadLockTest2() throws Exception{
		baozi = null;
		Thread consumerThread = new Thread(() -> {
			while(baozi == null){
				try {
					Thread.sleep(5000);
				} catch (Exception e) {
					e.printStackTrace();
				}
				System.out.println("没包子,进入等待...");
				Thread.currentThread().suspend();
			}
			System.out.println("买到包子,回家...");
		});
		//	开始线程
		consumerThread.start();
		//	3秒后,生产包子
		Thread.sleep(3000);
		baozi = new Object();
		consumerThread.resume();
		System.out.println("通知消费者");
	}

输出

通知消费者
没包子,进入等待...

2、wait/notify

wait/notify必须在同步代码快中执行,wait()执行后,会释放拿到的锁,不会导致notify()执行时拿不到锁的情况,示例代码:

	public void waitNotifyTest() throws Exception{
		baozi = null;
		Thread consumerThread = new Thread(() -> {
			while(baozi == null){
				System.out.println("没包子,进入等待...");
				synchronized (this) {
					try {
						this.wait();
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
			System.out.println("买到包子,回家...");
		});
		//	开始线程
		consumerThread.start();
		//	3秒后,生产包子
		Thread.sleep(3000);
		baozi = new Object();
		synchronized (this) {
			this.notify();
		}
		System.out.println("通知消费者");
	}

输出

没包子,进入等待...
通知消费者
买到包子,回家...

wait/notify依然对执行顺序有严格的要求,如果在wait()前执行notify(),那么线程会永远处于Waiting状态,示例代码:

	public void waitNotifyLockTest() throws Exception{
		baozi = null;
		Thread consumerThread = new Thread(() -> {
			while(baozi == null){
				System.out.println("没包子,进入等待...");
				try {
					Thread.sleep(5000);
				} catch (Exception e1) {
					e1.printStackTrace();
				}
				synchronized (this) {
					try {
						this.wait();
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				System.out.println("买到包子,回家...");
			}
		});
		//	开始线程
		consumerThread.start();
		//	3秒后,生产包子
		Thread.sleep(3000);
		baozi = new Object();
		synchronized (this) {
			this.notify();
		}
		System.out.println("通知消费者");
	}

输出

没包子,进入等待...
通知消费者

3、park/unPark

park/unPark对执行顺序没有要求,unPark()方法会对执行线程放出一个许可,当线程执行park()后,会一直查看是否有许可,获取到许可会继续执行,多次调用unPark()后,在调用park(),程序会直接执行。UNPark()的效果不会叠加。但是在同步代码块中使用时,park()方法无法释放锁。

正常执行的代码示例:

	public void parkUnParkTest() throws Exception{
		baozi = null;
		Thread consumerThread = new Thread(() -> {
			while(baozi == null){
				System.out.println("没包子,进入等待...");
				try {
					Thread.sleep(5000);
				} catch (Exception e1) {
					e1.printStackTrace();
				}
				LockSupport.park();
				System.out.println("已经收到有包子的通知,墨迹一会儿...");
				LockSupport.park();
				System.out.println("买到包子,回家...");
			}
		});
		//	开始线程
		consumerThread.start();
		//	3秒后,生产包子
		Thread.sleep(3000);
		baozi = new Object();
		System.out.println("通知消费者...");
		LockSupport.unpark(consumerThread);
		LockSupport.unpark(consumerThread);
		LockSupport.unpark(consumerThread);
		Thread.sleep(7000);
		System.out.println("通知消费者不买包子就离开...");
		LockSupport.unpark(consumerThread);
	}

输出

没包子,进入等待...
通知消费者...
已经收到有包子的通知,墨迹一会儿...
通知消费者不买包子就离开...
买到包子,回家...

无法释放锁的代码示例:

	public void parkUnParkBlockTest() throws Exception{
		baozi = null;
		Thread consumerThread = new Thread(() -> {
			while(baozi == null){
				System.out.println("没包子,进入等待...");
				try {
					//Thread.sleep(5000);
				} catch (Exception e1) {
					e1.printStackTrace();
				}
				synchronized (this) {
					LockSupport.park();
				}
				System.out.println("买到包子,回家...");
			}
		});
		//	开始线程
		consumerThread.start();
		//	3秒后,生产包子
		Thread.sleep(3000);
		baozi = new Object();
		synchronized (this) {
			System.out.println("通知消费者...");
			LockSupport.unpark(consumerThread);
		}
		
	}

输出

没包子,进入等待...

在同步代码块中park()执行后不会释放锁,但可先执行unPark(),先给线程一个许可,这个需要结合实际业务来看。

代码中使用while代替if进行判断,是为了避免伪唤醒。

文件共享

线程A写入数据到文件,其他线程可以读取到线程A写入的数据。

网络共享

与文件共享类似。

变量共享

利用内存,线程A写入到内存的一个变量中,其他线程可以读取。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值