线程间交互(wait notify)

1 线程间交互

线程间的交互,也就是相互通知,进而达到相互控制,java中线程间的交互要用到java.lang.Object的类的三个方法分别是wait,notify(),notifyAll,三个方法的调用必须在同步环境内调用,也就是线程获取了对象的锁后才能调用。
如果线程A持有线程B的对象的锁,多线程环境下只有当线程A获取了线程B的锁后(同步环境下)时,线程A才能调用B的wait,notify(),notifyAll方法。
wait()还有另外两个重载方法:
void wait(long timeout):等待直到其他线程调用此对象的 notify() 或 notifyAll() ,或者超过timeout时间量。
void wait(long timeout, int nanos):等待直到其他线程调用此对象的 notify() 或 notifyAll() ,或者其他某个线程中断当前线程,或者已超过某个实际时间量。
wait函数会释放锁。


    /**
      wait函数会立即释放对象锁,导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,才会被重新激活。
     * Causes the current thread to wait until another thread invokes the
     当前线程必须拥有交互对象的monitor(什么是monitor呢,会在后面讲,
     这里大家把monitor理解为一个对象,每个object只和一个monitor相关联,一个线程获取monitor之后,除非他主动释放
     其他线程无法获取monitor)。
     如果当前线程没有获取monitor,它将一直等待,直到其他线程调用该对象的notify,notifyAll唤醒当前线程。
     既然调用notify和notifyall能够唤醒线程,所以必定在该对象上存在一个线程链,
     当监听到调用了notify时,可以通知该对象上的线程链中的线程。
     * The current thread must own this object's monitor. The thread
     * releases ownership of this monitor and waits until another thread
     * notifies threads waiting on this object's monitor to wake up
     * either through a call to the {@code notify} method or the
     * {@code notifyAll} method. The thread then waits until it can
     * re-obtain ownership of the monitor and resumes execution.
     * 由于必须要获取monitor,所以wait调用必须在同步代码块内,且当前线程获取了对应object的monitor,如下所示。
     * * This method should only be called by a thread that is the owner
     * of this object's monitor. See the {@code notify} method for a
     * description of the ways in which a thread can become the owner of
     * a monitor.
     * As in the one argument version, interrupts and spurious wakeups are
     * possible, and this method should always be used in a loop:
     * <pre>
     *     synchronized (obj) {
     *         while (&lt;condition does not hold&gt;)
     *             obj.wait();
     *         ... // Perform action appropriate to condition
     *     }
     * </pre>
    public final void wait() throws InterruptedException {
        wait(0);
    }

void notify():唤醒在此对象监视器上等待的单个线程。
void notifyAll():唤醒在此对象监视器上等待的所有线程。
notify()和notifyAll()方法只是唤醒等待该对象的monitor的线程,并不决定哪个线程能够获取到monitor。

 /**
 调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;
 notify和notifyAll的调用也必须在获取对象monitor的状态下,函数会唤醒该对象线程链中的一个线程。
     * Wakes up a single thread that is waiting on this object's
     * monitor. If any threads are waiting on this object, one of them
     * is chosen to be awakened. The choice is arbitrary and occurs at
     * the discretion of the implementation. A thread waits on an object's
     * monitor by calling one of the {@code wait} methods.
     * <p>
     * The awakened thread will not be able to proceed until the current
     * thread relinquishes the lock on this object. The awakened thread will
     * compete in the usual manner with any other threads that might be
     * actively competing to synchronize on this object; for example, the
     * awakened thread enjoys no reliable privilege or disadvantage in being
     * the next thread to lock this object.
     * <p>
     * This method should only be called by a thread that is the owner
     * of this object's monitor. A thread becomes the owner of the
     * object's monitor in one of three ways:
     * <ul>
     * <li>By executing a synchronized instance method of that object.
     * <li>By executing the body of a {@code synchronized} statement
     *     that synchronizes on the object.
     * <li>For objects of type {@code Class,} by executing a
     *     synchronized static method of that class.
     * </ul>
     * <p>
     */
    public final native void notify();
/**
多个线程可以等待一个对象锁,当需要唤醒所有线程时,可以调用notifyAll。
调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程;
notifyAll函数会唤醒所有在等待object monitor的线程,最终能够获取monitor的只有一个线程。
     * Wakes up all threads that are waiting on this object's monitor. A
     * thread waits on an object's monitor by calling one of the
     * {@code wait} methods.
     * <p>
     * The awakened threads will not be able to proceed until the current
     * thread relinquishes the lock on this object. The awakened threads
     * will compete in the usual manner with any other threads that might
     * be actively competing to synchronize on this object; for example,
     * the awakened threads enjoy no reliable privilege or disadvantage in
     * being the next thread to lock this object.
     * <p>
     * This method should only be called by a thread that is the owner
     * of this object's monitor. See the {@code notify} method for a
     * description of the ways in which a thread can become the owner of
     * a monitor.
     *
     * @throws  IllegalMonitorStateException  if the current thread is not
     *               the owner of this object's monitor.
     * @see        java.lang.Object#notify()
     * @see        java.lang.Object#wait()
     */
    public final native void notifyAll();

注意:
在对象上调用wait()方法时,执行该代码的线程立即放弃它在对象上的锁。然而调用notify()时,并不会立即释放其锁。会在完成同步代码块后,释放锁,所以调用notify并不会导致立即释放锁。

//下面的代码每次都会先输出B的值
public class Demo3 {

	static Object object = new Object();
	public static void main(String[] args) {
		new Thread(new ThreadA()).start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		new Thread(new ThreadB()).start();
	}
	
	static class ThreadA implements Runnable{
		@Override
		public void run() {
			synchronized (object) {
				try {
					object.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
				System.out.println("ThreadA");
			}
		}
	}
	
	static class ThreadB implements Runnable{
		@Override
		public void run() {
			synchronized (object) {
				try {
					object.notify();
				} catch (Exception e) {
					e.printStackTrace();
				}
				
				System.out.println("ThreadB");
			}
		}
	}

}

如果调用某个对象的wait()方法,当前线程必须拥有这个对象的monitor(即锁),因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)
调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)
调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;
调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程;

2 生产者消费者

生产者消费者模型最常用的线程交互模型,当仓库物品为空时无法消费,当仓库满时无法生产。

package com.sync.demo;

import java.util.ArrayList;
import java.util.Random;

public class Demo4 {

	public static void main(String[] args) {
		ArrayList<String> list = new ArrayList<>();
		Thread con1 = new Thread(new Consumer(list));
		Thread con2 = new Thread(new Consumer(list));
		Thread con3 = new Thread(new Consumer(list));
		Thread pro = new Thread(new Productor(list));
		con1.start();
		con2.start();
		con3.start();
		pro.start();
	}
	
	static class Consumer implements Runnable{

		private ArrayList<String> list;
		
		public Consumer(ArrayList<String> list) {
			this.list = list;
		}

		private void consumer() {
			synchronized (list) {
				while (list.size() < 1) {
					System.out.println("=======仓库已空,无法消费,请等待======"+list.size());
					try {
						list.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
				try {
					Thread.sleep(70);
					list.remove(list.size() -1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("=======消费一个======剩余======"+Thread.currentThread().getName()+" "+list.size());
				list.notify();
				
			}
		}

		@Override
		public void run() {
			while (true) {
				consumer();
			}
		}
	
	}
	
	
	static class Productor implements Runnable{

		private ArrayList<String> list;
		
		
		public Productor(ArrayList<String> list) {
			this.list = list;
		}

		private void productor() {
			synchronized (list) {
				while (list.size() == 77) {
					System.out.println("=======仓库已满,停止生产======"+Thread.currentThread().getName()+" "+list.size());
					try {
						list.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
				try {
					Thread.sleep(50);
					list.add((new Random().nextInt(100))+"pro");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("=======生产一个======剩余======"+list.size());
				list.notify();
				
			}
		}

		@Override
		public void run() {
			while (true) {
				productor();
			}
		}
	
	}

}

结果:

=======生产一个======剩余======59
=======生产一个======剩余======60
=======生产一个======剩余======61
=======生产一个======剩余======62
=======生产一个======剩余======63
=======生产一个======剩余======64
=======生产一个======剩余======65
=======生产一个======剩余======66
=======生产一个======剩余======67
=======生产一个======剩余======68
=======生产一个======剩余======69
=======生产一个======剩余======70
=======生产一个======剩余======71
=======生产一个======剩余======72
=======生产一个======剩余======73
=======生产一个======剩余======74
=======生产一个======剩余======75
=======生产一个======剩余======76
=======生产一个======剩余======77
=======仓库已满,停止生产======Thread-3 77
=======消费一个======剩余======Thread-0 76
=======消费一个======剩余======Thread-0 75
=======消费一个======剩余======Thread-0 74
=======消费一个======剩余======Thread-0 73
=======消费一个======剩余======Thread-0 72
=======消费一个======剩余======Thread-0 71
=======消费一个======剩余======Thread-0 70
=======消费一个======剩余======Thread-0 69
=======消费一个======剩余======Thread-0 68
=======消费一个======剩余======Thread-0 67
=======消费一个======剩余======Thread-0 66
=======消费一个======剩余======Thread-0 65
=======消费一个======剩余======Thread-0 64
=======消费一个======剩余======Thread-0 63
=======消费一个======剩余======Thread-0 62
=======消费一个======剩余======Thread-0 61
=======消费一个======剩余======Thread-0 60
=======消费一个======剩余======Thread-0 59
=======消费一个======剩余======Thread-0 58
=======消费一个======剩余======Thread-0 57
=======消费一个======剩余======Thread-0 56
=======消费一个======剩余======Thread-0 55
=======消费一个======剩余======Thread-0 54
=======消费一个======剩余======Thread-0 53
=======消费一个======剩余======Thread-0 52
=======消费一个======剩余======Thread-0 51
=======消费一个======剩余======Thread-0 50
=======消费一个======剩余======Thread-0 49
=======消费一个======剩余======Thread-0 48
=======消费一个======剩余======Thread-1 47
=======消费一个======剩余======Thread-1 46
=======消费一个======剩余======Thread-1 45
=======消费一个======剩余======Thread-1 44
=======消费一个======剩余======Thread-1 43
=======消费一个======剩余======Thread-1 42
=======消费一个======剩余======Thread-1 41
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值