线程通信
来源:互联网 发布:java 工作流设计器 编辑:程序博客网 时间:2024/06/06 16:49
本文介绍线程通信的两种方式,一种是只有以synchronized方式实现线程同步时,可以采取的线程通信方式;另外一种是以ReentrantLock实现线程同步时,可以采取的线程通信方式。以下我们将分别介绍这两种实现线程通信的方式。
一、采用synchronized保证线程同步时如何实现线程通信
java Object类实现了wait(),notify(),notifyAll()三种方法,这三种方法是实现线程通信的关键所在。并且这三种方法必须由同步监视器(所谓同步监视器就是个锁,也就是synchronized(锁))调用,如果synchronized锁的对象和实现了wait(),notify(),notifyAll()这三种方法的对象不是一个的话,将会抛出java.lang.IllegalMonitorStateException异常。接下来我们了解下这三个方法是作用。
- wait():导致当前线程等待,直到其他线程调用该同步监视器(即锁)的notify()或notifyAll()方法时来唤醒该线程。同时调用wait()方法的当前线程会释放对该同步监视器的锁定(翻译成人话就是:当调用wait方法时,会释放这个同步锁,也就是synchronized锁定的那个对象,也就是调用wait()方法的对象)。
- notify():调用notify()方法时,会唤醒由于阻塞在同一个同步监视器调用了wait()方法的线程,翻译成书面语就是,唤醒在此同步监视器上等待的当个线程,如果在此同步监视器上阻塞着很多线程,则会选择一个唤醒,至于唤醒那个线程,这基于JVM对线程的调度。
- notifyAll():唤醒在此同步监视器上阻塞的所有线程。也就是唤醒所有线程由于调用了该同步监视器的await方法而导致阻塞。
wait(),notify(),notifyAll()这三种方法的调用必须在synchronized方法或者块中。
Demo:
代码:
public class TestNotify {
// 共享资源
private static int num;
// 同步监视器
private static Object obj = new Object();
static class produce extends Thread {
@Override
public void run() {
while (true) {
synchronized (obj) {
try {
if (num == 5) {
System.out.println("num == 5 produce await");
obj.wait();
}
num++;
System.out.println("produce thread num=" + num);
obj.notifyAll();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
static class consumer extends Thread {
@Override
public void run() {
while (true) {
synchronized (obj) {
try {
if (num == 0) {
System.out.println("num == 0 consumer await");
obj.wait();
}
num--;
System.out.println("consumer thread num=" + num);
obj.notifyAll();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
new produce().start();
new consumer().start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.exit(0);
}
}
运行结果:
produce thread num=1
produce thread num=2
produce thread num=3
produce thread num=4
produce thread num=5
num == 5 produce await
consumer thread num=4
consumer thread num=3
consumer thread num=2
produce thread num=3
produce thread num=4
produce thread num=5
num == 5 produce await
consumer thread num=4
produce thread num=5
num == 5 produce await
consumer thread num=4
produce thread num=5
二、采用ReentrantLock保证线程同步如何实现线程通信
由于采用Object作为同步监视器时,必须以synchronized的方式实现线程同步,但由于实际项目开发中,使用可重入锁ReentrantLock保证线程同步的方式比较多,在这种情况下就需要使用别的方式实现线程通信。java1.5提供了Condition对象来实现以Lock方式实现的线程同步时如何实现线程通信。condition替代了传统Object的wait(),notify(),notifyAll()方法,取而代之的是await(),signal(),signalAll()方法。这种方式可以具有多种等待结果集(我的理解就是同一把锁,可以new 多个Condition,每种Condition调用await()方法都可以导致当前线程等待);接下来一次介绍这三种方法。
- await():对应于Object的wait()方法,可以导致当前线程阻塞,直到其他线程调用了同一个Condition的signal()或signalAll()方法来唤醒当前线程。同时调用await()方法的当前线程会释放同步监视器(即:锁,该Condition对应的那把锁,也就是用于保证线程同步的那把锁)
- signal():用于唤醒相同Condition导致阻塞的其中一个线程,如果该Condition阻塞了多个线程,则选择一个线程唤醒。
- signalAll():用于唤醒相同Codition导致阻塞的所有线程。
编程注意事项:
- await(),signal(),signalAll()三种方法的调用必须在lock.lock()和lock.unlock()方法之间,说白了就是必须在线程具有相同同步监视器(锁)时,才可以调用这三种方法。
- 调用这三种方法的Condition对应的Lock 和保证线程同步的锁必须是同一个,否则会抛出java.lang.IllegalMonitorStateException异常。
code:
public class TestCondition
{
private static ReentrantLock lock1 = new ReentrantLock();
private static ReentrantLock lock2 = new ReentrantLock();
private static int num;
static Condition condition = lock1.newCondition();
static Condition condition2 = lock1.newCondition();
public static void main(String[] args) {
new produce().start();
new consumer().start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.exit(0);
}
static class produce extends Thread
{
@Override
public void run()
{
while(true)
{
// 如果保证线程同步的锁是lock2,则会java.lang.IllegalMonitorStateException异常
lock1.lock();
try
{
if (num == 5)
{
System.out.println("num == 5 produce await");
// 如果是condition2 则线程会一直阻塞下去,因为没有线程调用condition2的signal方法
condition.await();
}
num++;
System.out.println("produce thread num=" + num);
condition.signalAll();
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
lock1.unlock();
}
}
}
}
static class consumer extends Thread
{
@Override
public void run()
{
while(true)
{
lock1.lock();
try
{
if (num == 0)
{
System.out.println("num == 0 consumer await");
condition.await();
}
num--;
System.out.println("consumer thread num="+num);
condition.signalAll();
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
lock1.unlock();
}
}
}
}
}
运行结果:
produce thread num=1
produce thread num=2
produce thread num=3
produce thread num=4
produce thread num=5
num == 5 produce await
consumer thread num=4
consumer thread num=3
consumer thread num=2
consumer thread num=1
consumer thread num=0
num == 0 consumer await
三、线程通信的应用
线程通信是建立在多线程的基础上,没有多线程也无从谈起线程之间的通信;多线程最经典的模型是生产-消费者模型,试想一个共享资源队列,生产者向队列中添加,消费者从队列中移出,如果队列满了,则生产者必须等待消费者将商品从队列中移出,这样才能继续生产,但是在生产者等待的这段时间,必须释放掉对共享资源(即:队列)的占有权(锁),这样消费者才能拿到队列,并从队列中取出商品。所以这时就需要生产者和消费者之间要有通信,才能保证系统的更好的运转。
其实在编写上述小事例的代码时,完全可以通过阻塞队列来达到目的,当队列为空时,如果还从队列中取(take())则当前线程会一直阻塞,直到队列有数据;如果队列已满,还往队列中加(put())则当前线程阻塞。直到队列有空间。阻塞队列之所以能够实现,是因为阻塞队列的内部也是采用了线程通信的方式。
0 0
- 线程通信
- 线程通信
- 线程通信
- 线程通信
- 线程通信
- 线程通信
- 线程通信
- 线程通信
- 线程通信
- 线程通信
- 线程通信
- 线程通信
- 线程通信
- 线程通信
- 线程通信
- 线程通信
- 线程通信
- 线程通信
- 针对自定义标题栏拖动效果问题解决
- Phpstorm激活
- java学习笔记--文件传输io流
- 用 TensorFlow 做个聊天机器人
- Android通过Http连接MySQL 实现登陆/注册(数据库+服务器+客户端)
- 线程通信
- 0426-CSS三大特性之层叠性
- 1010. 一元多项式求导
- Jsoup学习
- iOS 名字按字母排序
- TK1学习笔记一:刷机
- caffe-windows 配置和cifar10数据集训练
- 【EJB】Developing EJB Applications -- Chapter 4(消息驱动Bean)
- java基础总结#多线程