线程间通信 Object/wait(),notify() 和 Lock/Condition/await(),signal()

 

基本前提知识:

一:Object/wait(), notify(), notifyAll()

  1:wait() 方法暂停当前线程,并立即释放对象锁;

  2:notify()/notifyAll() 方法唤醒其他等待该对象锁的线程,并在执行完同步代码块中的后续步骤后,释放对象锁

  3:notify()和notifyAll()的区别在于:

      notify只会唤醒其中一个线程,
        notifyAll则会唤醒全部线程。
    
    至于notify会唤醒哪个线程,是由线程调度器决定的。

例子:

public class TestWaitAndnotify {

    public static void main(String[] args) {
        demo2();
    }
    
    public static void demo2 () {
        final Object lock = new Object();
        Thread A = new Thread(new Runnable(){

            @Override
            public void run() {
                System.out.println("INFO: A 等待锁 ");
                synchronized (lock) {
                    System.out.println("INFO: A 得到了锁 lock");
                    System.out.println("A1");
                    try {
                        System.out.println("INFO: A 准备进入等待状态,放弃锁 lock 的控制权 ");
                        lock.wait();//挂起线程A 放弃锁 lock 的控制权
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("INFO: 有人唤醒了 A, A 重新获得锁 lock");
                    System.out.println("A2");
                    System.out.println("A3");
                }
            }
        });
        
        Thread B = new Thread(new Runnable() {

            @Override
            public void run() {
                 System.out.println("INFO: B 等待锁 ");
                synchronized (lock) {
                    System.out.println("INFO: B 得到了锁 lock");
                    System.out.println("B1");
                    System.out.println("B2");
                    System.out.println("B3");
                    System.out.println("INFO: B 打印完毕,调用 notify 方法 ");
                    lock.notify(); // notify()方法唤醒正在等待lock锁的线程A
                    System.out.println("线程 B do notify method 完毕");
                }
            }
        });
        
        A.start();
        B.start();
    } 
}

输出:

题外话: notify()方法唤醒等待锁对象的其他线程,这个notify()方法需要先执行完自己线程的后续代码,才会接着执行被唤醒的那个线程的代码,修改上面的例子看一下:

public class TestWaitAndnotify {

    public static void main(String[] args) {
        demo2();
    }
    
    public static void demo2 () {
        final Object lock = new Object();
        Thread A = new Thread(new Runnable(){

            @Override
            public void run() {
                System.out.println("INFO: A 等待锁 ");
                synchronized (lock) {
                    System.out.println("INFO: A 得到了锁 lock");
                    System.out.println("A1");
                    try {
                        System.out.println("INFO: A 准备进入等待状态,放弃锁 lock 的控制权 ");
                        lock.wait();//挂起线程A 放弃锁 lock 的控制权
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("INFO: 有人唤醒了 A, A 重新获得锁 lock");
                    System.out.println("A2");
                    System.out.println("A3");
                }
            }
        });
        
        Thread B = new Thread(new Runnable() {

            @Override
            public void run() {
                 System.out.println("INFO: B 等待锁 ");
                synchronized (lock) {
                    System.out.println("INFO: B 得到了锁 lock");
                    System.out.println("B1");
                    System.out.println("B2");
                    System.out.println("B3");
                    System.out.println("INFO: B 打印完毕,调用 notify 方法 ");
                    lock.notify(); // notify()方法唤醒正在等待lock锁的线程A
                    try {
                        Thread.sleep(10000); // 睡眠10秒钟
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程 B do notify method 完毕");
                }
            }
        });
        
        A.start();
        B.start();
    } 
}

 

打印完:INFO: B 打印完毕,调用 notify 方法  后面等待了10秒钟才开始执行。wait()方法是要等待notify()后续的代码才能开始。

 

生产者消费者例子:

import java.util.LinkedList;
import java.util.Queue;

public class ProducerAndConsumer {
    
    private final int MAX_LEN = 3;
    private Queue<Integer> queue = new LinkedList<Integer>();
    
    class Producer extends Thread {
        @Override
        public void run() {
            producer();
        }
        
        private void producer() {
            while(true) {
                synchronized (queue) {
                    if(queue.size() == MAX_LEN) {
                        System.out.println("当前队列满");
                        queue.notify();
                        try {
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    queue.add(1);
                    queue.notify();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("生产者又生产一条任务,当前队列长度为" + queue.size());
                    
                }
            }
        }
    }
    
    class Consumer extends Thread {
        @Override
        public void run() {
            consumer();
        }
        private void consumer() {
            while(true) {
                synchronized (queue) {
                    if(queue.size() == 0) {
                        System.out.println("当前队列为空");
                        queue.notify();
                        try {
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        
                    }
                    queue.poll();
                    queue.notify();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("消费者消费了一条任务,当前队列长度为" + queue.size());
                }
            }
        }
    }
    
    public static void main(String[] args) {
        ProducerAndConsumer pac = new ProducerAndConsumer();
        
        Producer producer = pac.new Producer();
        Consumer consumer = pac.new Consumer();
        producer.start();
        consumer.start();
    }
}

 

一:Lock/condition/await(), signal(), signalAll()

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

public class ProducerAndConsumer1 {
    
    private final int MAX_LEN = 3;
    private Queue<Integer> queue = new LinkedList<Integer>();

    final Lock lock = new ReentrantLock();
    final Condition producerSignal = lock.newCondition();
    final Condition consumerSignal = lock.newCondition();

    class Producer extends Thread {
        @Override
        public void run() {
            producer();
        }
        
        private void producer() {
            while(true) {
                lock.lock();
                try {
                    if(queue.size() == MAX_LEN) {
                        System.out.println("当前队列满");
                        try {
                            consumerSignal.signal();
                            producerSignal.await();    
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    queue.add(1);
                    consumerSignal.signal();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("生产者生产了一条任务,当前队列长度为" + queue.size());
                    
                } finally {
                    lock.unlock();
                }
            }

        }
    }
    
    class Consumer extends Thread {
        @Override
        public void run() {
            consumer();
        }
        
        private void consumer() {
            while(true) {
                lock.lock();
                try {
                    if(queue.size() == 0) {
                        System.out.println("当前队列为空");
                        try {
                            producerSignal.signal();
                            consumerSignal.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    queue.poll();
                    producerSignal.signal();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("消费者消费了一条任务,当前队列长度为" + queue.size());
                    
                } finally {
                    lock.unlock();
                }
            }

        }
    }
    
    public static void main(String[] args) {
        ProducerAndConsumer1 pac = new ProducerAndConsumer1();
        
        Producer producer = pac.new Producer();
        Consumer consumer = pac.new Consumer();
        producer.start();
        consumer.start();
    }

}

分析: 这里共享的变量是queue,生产者消费者都需要修改这个queue,为了保证安全,需要一个个的来,用lock,lock()就保证了每次只有一个线程来操作queue;

   在生产和消费的时候,需要关心queue.size()的大小,如果满了,就不能生产,这里用了producerSignal.await(),让生产者放弃锁,去等待消费者去消费,consumerSignal.signal()去唤醒消费者去消费,如果空了,就不能消费, 这里用consumerSignal.await(),让消费者放弃锁,去等待生产者去生产,producerSignal.signal()去唤醒生产者去生产。

 

posted @ 2019-12-26 18:24  myseries  阅读(719)  评论(0编辑  收藏  举报