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

来源:互联网 发布:凤岗数控车床编程培训 编辑:程序博客网 时间:2024/05/17 16:57
    多线程编程中,如果每个线程之间互相独立,那么将会使多线程带来的优势不能够很好地发挥出来。使用线程间通信,可以使得原先的互相独立的多个线程之间,能够很好地互相协作,使得系统之间的交互性得到提升,大大提高了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();//释放当前持有的锁                }            }        }    }

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

    上述两个示例,实现了最基本的等待/通知机制,从两个示例的运行结果可以看出,两种方式实现结果完全一致,所以这两种方法都可以用于实现线程间的通信;但是线程间的通信较为复杂,实际应用中需要注意很多比较容易踩的"坑";因此,需要多积累,多实践。     通常开发过程中,等待/通知机制的经典案例就是生产者/消费者模式,生产者消费模式又分为单生产者单消费者模式、单生产者多消费者模式、多生产者单消费者模式以及多生产者多消费者模式;每一种模式中需要注意的点不同,可能会遇到线程的"假死"和因为等待条件的改变而导致的异常;这一部分将会在另一片博文进行阐述!
阅读全文
0 0
原创粉丝点击