《Java多线程编程核心技术》--第3章--线程间通信

线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体。线程间的通信就是成为整体的必用方案之一,可以说,使线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时还会使程序员对各线程任务在处理的过程中进行有效的把控与监督。本章重点:

  1. 使用wait/notify实现线程间的通信
  2. 生产者/消费者模式的实现
  3. 方法join的使用
  4. ThreadLocal类的使用

一、等待/通知机制

本节介绍多个线程之间进行通信。

1.1 不使用等待/通知机制实现线程间通信

1.2 什么是等待/通知机制

1.3 等待/通知机制的实现

方法wait()的作用是使当前执行代码的线程进行等待,wait()方法是Object类的方法,该方法用来将当前线程置入“预执行队列”中,并且在wait()所在的代码行处停止执行,直到接到通知或被中断为止。在调用wait()方法之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法。在执行wait()方法后,当前线程释放锁。在从wait()返回前,线程与其他线程竞争重新获得锁。
方法notify()也要在同步方法或同步块中调用,即在调用前,线程也必须获得该对象的对象级别锁。该方法用来通知那些可能等待该对象锁的其他线程,如果有多个线程等待,则由线程规划器随机挑选出其中一个呈wait状态的线程,对其发出通知notify,并使它等待获取该对象的对象锁。在执行notify()方法后,当前线程不会马上释放该对象锁,呈wait状态的线程也并不能马上获取该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出synchronized代码块后,当前线程才会释放锁,而呈wait状态所在的线程才可以获取该对象锁。当第一个获得了该对象锁的wait线程运行完毕以后,它会释放掉该对象锁,此时如果该对象没有再次使用notify语句,则即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,还会继续阻塞在wait装,直到这个对象发出一个notify或notifyAll()。

白话说多线程等待通知机制
https://baijiahao.baidu.com/s?id=1585382993787729935&wfr=spider&for=pc

1.4 方法wait()锁释放与notify()锁不释放


1.5 当interrupt方法遇到wait方法


1.6 只通知一个线程


1.7 唤醒所有线程


1.8 方法wait(long)的使用


1.9 通知过早


1.10 等待wait的条件发生变化


1.11 生产者/消费者模式实现

1.一生产与一消费:操作值

public class Producer {
	private Object lock;

	public Producer(Object lock) {
		this.lock = lock;
	}

	public void setValue() {
		try {
			synchronized (lock) {
				if (!ValueObject.value.equals("")) {
					lock.wait();
				}
				String value = System.currentTimeMillis() + "_" + System.nanoTime();
				System.out.println("set的值:" + value);
				ValueObject.value = value;
				lock.notify();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
public class Consumer {
	private Object lock;

	public Consumer(Object lock) {
		this.lock = lock;
	}

	public void getValue() {
		try {
			synchronized (lock) {
				if (ValueObject.value.equals("")) {
					lock.wait();
				}
				System.out.println("get的值:"+ValueObject.value);
				ValueObject.value = "";
				lock.notify();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
public class ThreadProducer extends Thread {
	private Producer producer;

	public ThreadProducer(Producer producer) {
		this.producer = producer;
	}

	@Override
	public void run() {
		try {
			while (true) {
				producer.setValue();
				Thread.sleep(1000);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
public class ThreadConsumer extends Thread {
	private Consumer consumer;

	public ThreadConsumer(Consumer consumer) {
		this.consumer = consumer;
	}

	@Override
	public void run() {
		try {
			while (true) {
				consumer.getValue();
				Thread.sleep(1000);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
public class Client {
	public static void main(String[] args) {
		Object lock = new Object();
		Producer producer = new Producer(lock);
		Consumer consumer = new Consumer(lock);

		ThreadProducer threadProducer = new ThreadProducer(producer);
		ThreadConsumer threadConsumer = new ThreadConsumer(consumer);
		threadProducer.start();
		threadConsumer.start();
	}
}

1.12 通过管道进行线程间通信:字节流

管道流(pipeStream)是一种特殊的流,用于在不同线程间直接传送数据。一个线程发送数据到输出管道,另一个线程从输出管道中读取数据。通过使用管道,实现不同线程间的通信,而无须借助类似临时文件之类的东西。




1.13 通过管道进行线程间通信:字符流


1.14 实战:等待/通知之交叉备份


二、join方法的使用

很多情况下,主线程创建并启动子线程,如果子线程要进行大量的耗时运算,主线程往往将早于子线程结束之前结束。这是,如果主线程想等待子线程执行完成之后在结束,比如子线程处理一个数据,主线程要取得这个数据中的值,就要用到join()方法了。方法join()的作用是等待线程对象销毁。



2.1 学习join方法前的铺垫




2.2 用join()方法来解决



方法join的作用是使所属的线程对象x正常执行run()方法中的任务,而使当前线程z进行无限期的阻塞,等待线程x销毁后再继续执行线程z后面的代码。
方法join具有使线程排队运行的作用,有些类似同步的运行效果。join与synchronized的区别是:join在内部使用wait()方法进行等待,而synchronized关键字使用的是“对象监视器”原理做为同步。

2.3 方法join与异常





2.4 方法join(long)的使用





2.5 方法join(long)与sleep(long)的区别






2.6 方法join()后面的代码提前运行:出现意外




2.7 方法join()后面的代码提前运行:解释意外





三、类ThreadLocal的使用



3.1 方法get()与null



3.2 验证线程变量的隔离性



3.3 解决get()返回null问题





3.4 再次验证线程变量的隔离性





四、类InheritableThreadLocal的使用


4.1 值继承


4.2 值继承再修改

线程与线程之间不是独立的个体,它们彼此之间可以互相通信和协作。
方法wait()的作用是使当前执行代码的线程进行等待,wait()方法是Object类的方法。该方法用来将当前线程置入“预执行队列”中,并且在wait()所在代码行处停止执行,直到接到通知或被中断为止。在调用wait()之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步代码块中调用wait()方法。在执行wait()方法后,当前线程释放锁。在从wait()返回前,线程与其他线程竞争重新获得锁。如果调用wait()时没有持有适当的锁,则抛出IllegalMonitorStateException,它是RuntimeException的一个子类,因此,不需要try-catch语句进行捕捉异常。
方法notify()也要在同步方法或同步块中调用,即在调用前,线程也必须获得该对象的对象级别锁。如果调用notify()时没有持有适当的锁,也会抛出IllegalMonitorStateException。该方法用来通知那些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则由线程规划器随机挑选处一个呈wait状态的线程,对其发出通知notify,并使它等待获取该对象的对象锁。需要说明的是,在执行notify()方法后,当前线程不会马上释放该对象锁,呈wait状态的程也并不能马上获取该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出synchronized代码块后,当前线程才会释放锁,而呈wait()状态所在的线程才可以获取该对象锁。当第一个获得了该对象锁的wait线程运行完毕以后,它会释放掉该对象锁,此时如果该对象没有再次使用notify语句,则即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,还会继续阻塞在wait状态,直到这个对象发出一个notify或notify all。
用一句话来总结一下wait和notify:wait使线程停止运行,而notify使停止的线程继续运行。
对象监视器也就是同步锁





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值