多线程中的线程间通信及等待/通知机制的两种实现方式

多线程编程中,如果每个线程之间互相独立,那么将会使多线程带来的优势不能够很好地发挥出来。使用线程间通信,可以使得原先的互相独立的多个线程之间,能够很好地互相协作,使得系统之间的交互性得到提升,大大提高了CPU利用率,从而完成一些复杂的多线程功能模块。

多线程间的通信一般采取等待/通知机制进行实现。见名知意,等待通知就是处于等待状态的线程需要由其他线程发出通知,从而可以再次获得CPU资源,执行之前没有执行完的任务。

Java中,常用的实现等待/通知机制的两种方式是:
一、在使用synchronized关键字实现的同步方法或同步代码块中,由被上锁的对象调用其自身的wait()方法和notify()方法以及notifyAll()方法,进行实现等待/通知机制;
二、在使用ReentrantLock类的对象实现的同步方法或同步代码块中,使用Contion类的对象的await()方法和signal()方法以及signalAll()方法,进行实现等待/通知机制;

在实现等待/通知机制之前,要注意以下事项:
一、wait()方法和notify()方法以及notifyAll()方法的使用必须在同步方法或者同步代码块中,因为这三个方法需要获得监视器对象的对象级别的锁;同样地,await()方法和signal()方法以及signalAll()方法的使用必须在其所属的Contion类的对象所关联的ReentrantLock对象(锁对象)所作用的方法或者代码块中;

二、wait()方法和await()方法的调用有两个作用:1、使当前线程由运行状态变为等待状态2、释放当前线程持有的锁;

三、notify()方法和signal()方法的调用只有一个作用:唤醒因为wait()或await()而处于等待状态的线程;

synchronized同步中的等待/通知机制实现,示例代码:

package com.WaitAndNotify;
public class CommunicationInSynchronized {
    public static void main(String[] args) {
        String lock = "";//作为synchronized的对象监视器
        CommunicationInSynchronized cis = new CommunicationInSynchronized ();//产生实例,用于创建内部类实例
        Thread threadW = new Thread(cis.new ThreadW(lock));//实例化等待线程
        threadW.setName("W");//给等待线程命名
        threadW.start();//启动等待线程
        Thread threadN = cis.new ThreadN(lock);//实例化通知线程
        threadN.setName("N");//给通知线程命名
        threadN.start();//启动通知线程
    }
//实现Runnable接口的等待线程类
class ThreadW implements Runnable{
    private String lock;
    public ThreadW(String lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        synchronized(lock) {
        try {
            System.out.println(
                Thread.currentThread().getName() + 
                " begin wait time:" + System.currentTimeMillis());
                lock.wait();//当前线程进入等待队列,同时释放锁
            } catch (InterruptedException e) {
                e.printStackTrace();
            }             
        System.out.println( Thread.currentThread().getName()
         +" end wait time:" + System.currentTimeMillis());
        }
    }
}
//通知线程类
class ThreadN extends Thread{
    private String lock;
    public ThreadN(String lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        synchronized(lock) {
            try {
                System.out.println(
                Thread.currentThread().getName() + 
                " begin notify time:" + System.currentTimeMillis());
                lock.notify();//通知等待队列中的线程,使其进入就绪队列,等待获取锁
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() +
             " end notify time:" + System.currentTimeMillis());
        }
    }
}
}

运行结果如下图所示:
这里写图片描述
ReentrantLock锁同步中的等待/通知机制实现,示例代码2:

package com.waitAndNotify;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class CommunicationInReentrantLock {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();//同步锁对象
        Condition condition = lock.newCondition();//对应于同步锁对象的用于执行等待和通知命令的对象
        CommunicationInReentrantLock cis = new CommunicationInReentrantLock ();//实例化对象,用于创建内部类的实例
        Thread threadW = new Thread(cis.new ThreadW(lock,condition));//实例化等待线程
        threadW.setName("W");//命名等待线程
        threadW.start();//启动等待线程
        Thread threadN = cis.new ThreadN(lock,condition);//实例化通知线程
        threadN.setName("N");//命名通知线程
        threadN.start();//启动通知线程
    }
//实现Runnable接口的等待线程类
class ThreadW implements Runnable{
    private Lock lock;
    private Condition condition;
    public ThreadW(ReentrantLock lock,Condition condition) {
        this.lock = lock;
        this.condition = condition;
    }
    @Override
    public void run() {
        lock.lock();//上锁
        try {
            System.out.println(Thread.currentThread().getName()
 + " begin wait time:" + System.currentTimeMillis());
            condition.await();//使当前线程进入等待队列,同时释放当前持有的锁
            System.out.println(Thread.currentThread().getName() 
 + " end wait time:" + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();//释放锁
        }   
    }   
}
//通知线程类
class ThreadN extends Thread{
    private Lock lock;
    private Condition condition;
    public ThreadN(ReentrantLock lock,Condition condition) {
        this.lock = lock;
        this.condition = condition;
    }
    @Override
    public void run() {
        lock.lock();//上锁
        try {
            System.out.println(Thread.currentThread().getName() + 
            " begin notify time:" + System.currentTimeMillis());
            condition.signal();//通知等待队列中的线程,使其进入就绪队列,准备获取锁
            Thread.sleep(3000); 
            System.out.println(
            Thread.currentThread().getName() +
             " end notify time:" + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();//释放当前持有的锁
        }
    }
}
}

运行结果如下图所示:
这里写图片描述

上述两个示例,实现了最基本的等待/通知机制,从两个示例的运行结果可以看出,两种方式实现结果完全一致,所以这两种方法都可以用于实现线程间的通信;但是线程间的通信较为复杂,实际应用中需要注意很多比较容易踩的”坑”;因此,需要多积累,多实践。

通常开发过程中,等待/通知机制的经典案例就是生产者/消费者模式,生产者消费模式又分为单生产者单消费者模式、单生产者多消费者模式、多生产者单消费者模式以及多生产者多消费者模式;每一种模式中需要注意的点不同,可能会遇到线程的”假死”和因为等待条件的改变而导致的异常;这一部分将会在另一篇博文进行阐述!

猜你喜欢

转载自blog.csdn.net/boker_han/article/details/78579986