Java多线程编程-(4)多线程中的线程间通信及等待/通知机制的两种实现方式

多线程编程中,如果每个线程之间互相独立,那么将会使多线程带来的优势不能够很好地发挥出来。使用线程间通信,可以使得原先的互相独立的多个线程之间,能够很好地互相协作,使得系统之间的交互性得到提升,大大提高了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同步中的等待/通知机制实现,示例代码为ThreadW,ThreadN;ReentrantLock锁同步中的等待/通知机制实现,示例代码为ThreadA,ThreadB:

package com.waitandnotify;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author wanjiadong
 * @description
 * @date Create in 16:35 2019/1/2
 */
public class CommonSynchronized {

    class ThreadW extends Thread {
        private String lock;

        public ThreadW(String lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            synchronized (lock) {
                System.out.println("begin ThreadW time:"+System.currentTimeMillis());
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("end ThreadW time:"+System.currentTimeMillis());
            }
        }
    }

    class ThreadN extends Thread {
        private String lock;

        public ThreadN(String lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            synchronized (lock) {
                System.out.println("begin ThreadN time:"+System.currentTimeMillis());
                lock.notify();
                try {
                    Thread.sleep(5000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("end ThreadN time:"+System.currentTimeMillis());
            }
        }
    }

    public static void main(String[] args) {
        CommonSynchronized commonSynchronized = new CommonSynchronized();
        String lock = "lock";

        ThreadW threadW = commonSynchronized.new ThreadW(lock);
        ThreadN threadN = commonSynchronized.new ThreadN(lock);
        threadW.start();
        threadN.start();

        ReentrantLock reentrantLock = new ReentrantLock();
        //多条件
        Condition condition1 = reentrantLock.newCondition();
        Condition condition2 = reentrantLock.newCondition();
        ThreadA threadA = commonSynchronized.new ThreadA(reentrantLock, condition1);
        ThreadB threadB = commonSynchronized.new ThreadB(reentrantLock, condition1);
        ThreadC threadC = commonSynchronized.new ThreadC(reentrantLock, condition2);
        ThreadD threadD = commonSynchronized.new ThreadD(reentrantLock, condition2);

        threadA.start();
        threadB.start();
        threadC.start();
        threadD.start();
    }

    class ThreadA extends Thread {
        private Lock lock;
        private Condition condition;

        public ThreadA(Lock lock, Condition condition) {
            this.lock = lock;
            this.condition = condition;
        }

        @Override
        public void run() {
            lock.lock();

            System.out.println("ThreadA begin time:"+System.currentTimeMillis());
            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("ThreadA end time:"+System.currentTimeMillis());

            lock.unlock();
        }
    }

    class ThreadB extends Thread {
        private Lock lock;
        private Condition condition;

        public ThreadB(Lock lock, Condition condition) {
            this.lock = lock;

            this.condition = condition;
        }

        @Override
        public void run() {
            lock.lock();
            System.out.println("ThreadB begin time:"+System.currentTimeMillis());

            condition.signal();
            try {
                Thread.sleep(5000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("ThreadB end time:"+System.currentTimeMillis());

            lock.unlock();
        }
    }

    class ThreadC extends Thread {
        private Lock lock;
        private Condition condition;

        public ThreadC(Lock lock, Condition condition) {
            this.lock = lock;
            this.condition = condition;
        }

        @Override
        public void run() {
            lock.lock();

            System.out.println("ThreadC begin time:"+System.currentTimeMillis());
            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("ThreadC end time:"+System.currentTimeMillis());

            lock.unlock();
        }
    }

    class ThreadD extends Thread {
        private Lock lock;
        private Condition condition;

        public ThreadD(Lock lock, Condition condition) {
            this.lock = lock;

            this.condition = condition;
        }

        @Override
        public void run() {
            lock.lock();
            System.out.println("ThreadD begin time:"+System.currentTimeMillis());

            condition.signal();
            try {
                Thread.sleep(5000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("ThreadD end time:"+System.currentTimeMillis());

            lock.unlock();
        }
    }
}

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

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

猜你喜欢

转载自blog.csdn.net/baidu_20608025/article/details/85625564