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,其他的按之前的思路就可以了。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
嗨!很高兴回答你关于Java并发编程的问题。请问你想知道什么方面的内容呢?我可以分享一些学习笔记和建议给你。 1. 并发编程基础:了解并发编程的基本概念,如线程、进程、锁、同步等。学习Java中的并发编程模型以及相关的API,如Thread、Runnable、Lock、Condition等。 2. 线程安全性:学习如何保证多线程环境下的数据安全性,了解共享资源的问题以及如何使用同步机制来防止数据竞争和并发问题。 3. 线程的通信:掌握线程的通信方式,如使用wait/notify机制、Lock/Condition等来实现线程的协调与通信。 4. 并发容器:学习并发容器的使用,如ConcurrentHashMap、ConcurrentLinkedQueue等。了解它们的实现原理以及在多线程环境下的性能特点。 5. 并发工具类:熟悉Java提供的并发工具类,如CountDownLatch、CyclicBarrier、Semaphore等,它们可以帮助你更方便地实现线程协作。 6. 并发编程模式:学习一些常见的并发编程模式,如生产者-消费者模式、读者-写者模式、线程池模式等。了解这些模式的应用场景和实现方式。 7. 性能优化与调试:学习如何分析和调试多线程程序的性能问题,了解一些性能优化的技巧和工具,如使用线程池、减少锁竞争、避免死锁等。 这些只是一些基本的学习笔记和建议,Java并发编程是一个庞大而复杂的领域,需要不断的实践和深入学习才能掌握。希望对你有所帮助!如果你有更具体的问题,欢迎继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值