java并发编程-线程间协作的两种方式:wait、notify、nitifyAll和Condition

1.问题引入

         线程之间也是需要协作的,比如生产者-消费者模型:当队列满时, 生产者需要等待队列中有空间才能够向里面放入物品,而在等待的时间内,生产者必须释放对资源(队列)的占有权。因为如果生产者不释放对资源的占有权,那么消费者就无法从队列中取出物品,就不会让队列有空间,进而生产者就会一直无限等待下去。因此,一般情况下,当队列满时,会让生产者交出占有权,并进入挂起状态。然后等待消费者消费了物品,再通知生产者队列有空间了。同样的,当队列为空时,消费者也必须等待,等待生产者通知它队列中有物品了。这种相互通信的过程就是线程间的协作


2.wait()、notify()、notifyAll()

         wait()、notify()、notifyAll()是Object类中的方法

    public final native void notify();

    public final native void notifyAll();

    public final native void wait(long timeout) throws InterruptedException;
      可以看出以下几点:

      (1)wait()、notify()、notifyAll()都是native方法,并且都为final方法,无法被重写

      (2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的锁(monitor)

      (3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程。

      (4)调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程。

       注意:为何wait()、notify()、notifyAll()不是定义在Thread类中,而是在Object中?

                由于每个对象都拥有monitor(锁),所以让当前线程等待某个对象的锁,当然应该通过这个对象来操作了。而不是使用当前线程来操作,因为当前线程可能会等待多个线程的锁,如果通过线程来操作,会非常复杂。

        1>wait

       如果调用某个对象的wait()方法,则当前线程必须拥有这个对象的monitor(即锁),因此调用wait()方法必须在同步代码块或同步方法中。调用wait()方法,相当于让当前线程交出此对象的monitor,然后进入等待状态,等待后续再次获得此对象的锁(Thread类中的sleep方法使当前线程暂停执行一段时间,从而让其他线程有机会执行,但是它并不释放对象锁)


      2>notify

         notify方法能够唤醒一个正在等待该对象的monitor线程,当有多个线程都在等待该对象的monitor的话,则只能唤醒其中一个线程,具体唤醒哪一个不确定。同样的调用某个对象的notify方法,当前线程也必须拥有这个对象的monitor,因此调用notify方法必须在同步代码块或者同步方法中进行


      3>notifyAll

        notifyAll方法能够唤醒所有正在等待该对象的monitor的线程。注意:此处唤醒不等于所有线程都获得该对象的monitor,至于哪个等待的线程能够获得就不确定了。

public class Test
{
	private static Object object = new Object();
	public static void main(String[] args) 
	{
		new Thread(new Runnable() {
			@Override
			public void run() 
			{
				synchronized(object)
				{
					try 
					{
						object.wait();
						System.out.println("thread 0 get lock");
					}
					catch (InterruptedException e) 
					{
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}).start();
		
		new Thread(new Runnable()
		{
			@Override
			public void run() 
			{
				synchronized(object)
				{
					System.out.println("thread 1 get lock");
					object.notify();
					System.out.println("thread 1 release lock");
				}
			}
			
		}).start();
	}
}
           结果为:

thread 1 get lock
thread 1 release lock
thread 0 get lock


2.Condition

    用来代替传统Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition的await(),signal()这种方式实现线程间协作更加安全和高效。通常情况下推荐使用Condition。

    注意:

    (1)Condition是一个接口,基本的就是await()和signal()方法

    (2)Condition依赖于Lock接口,生成一个Condition的代码就是lock.newCondition()

    (3)调用Condition的await()和signal()方法,都必须在lock保护之内,也就是说必须在lock.lock()和lock.unlock()之间才可以使用


3.生产者-消费者模型实现

    (1)使用wait和notify

class ProducerCustomer
{
	private Queue<Integer> queue = new LinkedList<Integer>();
	private int size = 3;
	
	public void produce(int i)
	{
		synchronized (queue) 
		{
			try 
			{
				while(queue.size() >= size)
				{
					queue.wait();
					System.out.println("producer get monitor");
				}
				
				queue.offer(i);
				queue.notify();	
			}
			catch (InterruptedException e) 
			{
				e.printStackTrace();
			}
		}
	}
	public int consume()
	{
		int ans = 0 ;
		synchronized(queue)
		{
			try 
			{
				while (queue.size() == 0)
				{
					queue.wait();
					System.out.println("consumer get monitor");
				}
				
				ans = queue.poll();
				queue.notify();
			}
			catch (InterruptedException e) 
			{
				e.printStackTrace();
			}
		}
		return ans;
	}
}
public static void main(String[] args) 
	{
		final ProducerCustomer pc = new ProducerCustomer();
		
		new Thread(new Runnable() {
			@Override
			public void run() 
			{
				for(int i=0; i<8; i++)
					pc.produce(i);
			}
		}).start();
		
		new Thread(new Runnable() {
			@Override
			public void run() 
			{
				for(int i=0; i<8; i++)
					System.out.println(pc.consume());
			}
		}).start();
	}
              结果为:

0
producer get monitor
producer get monitor
1
2
3
4
producer get monitor
consumer get monitor
5
6
7

    (2)使用Condition
class ProducerCustomer
{
	private Queue<Integer> queue = new LinkedList<Integer>();
	private int size = 3;
	private Lock lock = new ReentrantLock();
	private Condition notFull = null;
	private Condition notEmpty = null;
	
	public ProducerCustomer() 
	{
		notFull = lock.newCondition();
		notEmpty = lock.newCondition();
	}
	public void produce(int i)
	{
		lock.lock();
		try
		{
			while(queue.size()>=size)
			{
				System.out.println("数据满了");
				notFull.await();
			}
			
			queue.offer(i);
			notEmpty.signal();
		}
		catch (InterruptedException e) 
		{
			e.printStackTrace();
		}
		finally
		{
			lock.unlock();
		}
	}
	public int consume()
	{
		int ans = 0;
		
		lock.lock();
		try
		{
			while(queue.isEmpty())
			{
				System.out.println("数据为空");
				notEmpty.await();
			}
			
			ans = queue.poll();
			notFull.signal();
		} 
		catch (InterruptedException e) 
		{
			e.printStackTrace();
		}
		finally
		{
			lock.unlock();
		}
		
		return ans;
	}
}
	public static void main(String[] args) 
	{
		final ProducerCustomer pc = new ProducerCustomer();
		
		new Thread(new Runnable() {
			@Override
			public void run() 
			{
				for(int i=0; i<8; i++)
					pc.produce(i);
			}
		}).start();
		
		new Thread(new Runnable() {
			@Override
			public void run() 
			{
				for(int i=0; i<8; i++)
					System.out.println(pc.consume());
			}
		}).start();
	}
      结果为:

数据满了
0
1
2
数据为空
数据满了
3
4
5
6
7


          简单的说,编写需要通信的线程,实际上就是将if改成while,其他的按之前的思路就可以了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值