javaSE-多线程间通信-生产者消费者例子

单生产者和单消费者。等待唤醒机制

class Resource
{
	//定义一个商品都有名字。
	private String name;
	//定义一个商品的编号。
	private int count = 1;

	//定义用来判断是否有商品的标记。
	private boolean flag = false;
	
	public synchronized  void set(String name)
	{
		if(flag)
			try{wait();}catch(Exception e){}
		this.name = name+"--"+count;
		count++;

		System.out.println(Thread.currentThread().getName()+"---生产了,"+this.name);
		//将标记改为true。
		flag = true;
		notify();//唤醒等待的线程。 
	}
	public synchronized void get()
	{
		if(!flag)
			try{wait();}catch(Exception e){}
		System.out.println(Thread.currentThread().getName()+"-------消费了....."+this.name);
		flag = false;
		notify();
	}
}

//定义生产者的任务。
class Producer implements Runnable
{
	private Resource r;
	Producer(Resource r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			r.set("蛋糕");
		}
	}

}


//定义消费者的任务。
class Consumer implements Runnable
{
	private Resource r;
	Consumer(Resource r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			r.get();
		}
	}
}


class ThreadDemo_Producer_Consumer 
{
	public static void main(String[] args) 
	{
		Resource r = new Resource();
		Producer pro = new Producer(r);
		Consumer con = new Consumer(r);
		Thread t1 = new Thread(pro);
		Thread t2 = new Thread(con);
		t1.start();
		t2.start();
	}
}

多生产者和多消费者。等待唤醒机制(低效)

/*

多生产者和多消费者。等待唤醒机制。


产生了两个问题:

1,出现了多次连续生产,未消费,或者一个商品被消费多次。
	必须要每一个被唤醒的线程判断一次标记,所以将if判断改为while判断。

2,出现了死锁。
	本方唤醒了本方,导致了所以的线程都等待了。
	解决方式就是,唤醒所有等待的线程。这样既唤醒了本方也唤醒对方。

虽然解决了多生产消费的问题,但是有些低效。



*/


class Resource
{
	//定义一个商品都有名字。
	private String name;
	//定义一个商品的编号。
	private int count = 1;

	//定义用来判断是否有商品的标记。
	private boolean flag = false;
	
	public synchronized  void set(String name)//
	{

		while(flag)
			try{wait();}catch(Exception e){}//t1,t2
		this.name = name+"--"+count;//蛋糕1  蛋糕2 蛋糕3
		count++;

		System.out.println(Thread.currentThread().getName()+"---生产了,"+this.name);//生产蛋糕1 生产蛋糕2 生产蛋糕3
		//将标记改为true。
		flag = true;
		notifyAll();//唤醒等待的线程。 
	}
	public synchronized void get()//
	{
		while(!flag)
			try{wait();}catch(Exception e){}//t3.t4
		System.out.println(Thread.currentThread().getName()+"-------消费了....."+this.name);//消费蛋糕1
		flag = false;
		notifyAll();
	}
}

//定义生产者的任务。
class Producer implements Runnable
{
	private Resource r;
	Producer(Resource r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			r.set("蛋糕");
		}
	}

}


//定义消费者的任务。
class Consumer implements Runnable
{
	private Resource r;
	Consumer(Resource r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			r.get();
		}
	}
}


class ThreadDemo_Producer_Consumer2 
{
	public static void main(String[] args) 
	{
		Resource r = new Resource();
		Producer pro = new Producer(r);
		Consumer con = new Consumer(r);

		//创建两个生产者线程。
		Thread t1 = new Thread(pro);
		Thread t2 = new Thread(pro);
		//创建两个消费者线程。
		Thread t3 = new Thread(con);
		Thread t4 = new Thread(con);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}


jdk1.5之后新的解决方法

java.util.concurrent.locks
接口 Lock
所有已知实现类:
ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock

java.util.concurrent.locks
类 ReentrantLock

java.lang.Object
  继承者 java.util.concurrent.locks.ReentrantLock
所有已实现的接口:
Serializable, Lock
一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。

java.util.concurrent.locks
接口 Condition

Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
方法摘要 
 void await() 
          造成当前线程在接到信号或被中断之前一直处于等待状态。 
 boolean await(long time, TimeUnit unit) 
          造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。 
 long awaitNanos(long nanosTimeout) 
          造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。 
 void awaitUninterruptibly() 
          造成当前线程在接到信号之前一直处于等待状态。 
 boolean awaitUntil(Date deadline) 
          造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。 
 void signal() 
          唤醒一个等待线程。 
 void signalAll() 
          唤醒所有等待线程。 


用显式的锁锁定代码 代替老版的同步代码块或同步函数锁定任务

class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m() { 
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }
 

新版解决方案

class Resource
{
	//定义一个商品都有名字。
	private String name;
	//定义一个商品的编号。
	private int count = 1;

	//定义用来判断是否有商品的标记。
	private boolean flag = false;

	
//	**************************************************
	//根据jdk1.5 版本的特性,创建一个锁对象。比同步的隐式锁操作要更加的面向对象。提供了显示的锁操作。
	final Lock lock = new ReentrantLock();//互斥锁。
	//通过lock锁获取监视器方法对象。Condition 负责生产者的监视操作。
	final Condition producer = lock.newCondition();

	//在创建一个监视器方法对象。负责消费者的监视操作。
	final Condition consumer = lock.newCondition();
//	***************************************************

public  void set(String name)//
	{
		//通过锁对象进行显示的获取锁操作。
		lock.lock();
		try
		{
			while(flag)
				try{producer.await();}catch(Exception e){}//t1,t2
			this.name = name+"--"+count;//蛋糕1  蛋糕2 蛋糕3
			count++;

			System.out.println(Thread.currentThread().getName()+"---生产了,"+this.name);//生产蛋糕1 生产蛋糕2 生产蛋糕3
			//将标记改为true。
			flag = true;
			consumer.signal();//唤醒等待的线程。 
		}
		finally{
			//释放锁。
			lock.unlock();//定义在finally中,要求一定被释放。
		}
	}
	public  void get()//
	{
		lock.lock();
		try
		{
			while(!flag)
				try{consumer.await();}catch(Exception e){}//t3.t4
			System.out.println(Thread.currentThread().getName()+"------++++++-消费了....."+this.name);//消费蛋糕1
			flag = false;
			producer.signal();
		}
		finally
		{
			lock.unlock();
		}
	}
}

//定义生产者的任务。
class Producer implements Runnable
{
	private Resource r;
	Producer(Resource r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			r.set("蛋糕");
		}
	}

}


//定义消费者的任务。
class Consumer implements Runnable
{
	private Resource r;
	Consumer(Resource r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			r.get();
		}
	}
}

class ThreadDemo_Producer_Consumer3 
{
	public static void main(String[] args) 
	{
		Resource r = new Resource();
		Producer pro = new Producer(r);
		Consumer con = new Consumer(r);

		//创建两个生产者线程。
		Thread t1 = new Thread(pro);
		Thread t2 = new Thread(pro);
		//创建两个消费者线程。
		Thread t3 = new Thread(con);
		Thread t4 = new Thread(con);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

系统提供的例子

作为一个示例,假定有一个绑定的缓冲区,它支持 put 和 take 方法。如果试图在空的缓冲区上执行 take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行 put 操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待 set 中保存 put 线程和 take 线程,这样就可以在缓冲区中的项或空间变得可用时利用最佳规划,一次只通知一个线程。可以使用两个 Condition 实例来做到这一点。
class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition(); 
   final Condition notEmpty = lock.newCondition(); 

   final Object[] items = new Object[100];
   int putptr, takeptr, count;

   public void put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length) 
         notFull.await();
       items[putptr] = x; 
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();
     } finally {
       lock.unlock();
     }
   }

   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0) 
         notEmpty.await();
       Object x = items[takeptr]; 
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();
       return x;
     } finally {
       lock.unlock();
     }
   } 
 }


Condition 实现可以提供不同于 Object 监视器方法的行为和语义,比如受保证的通知排序,或者在执行通知时不需要保持一个锁。如果某个实现提供了这样特殊的语义,则该实现必须记录这些语义。

注意,Condition 实例只是一些普通的对象,它们自身可以用作 synchronized 语句中的目标,并且可以调用自己的 wait 和 notification 监视器方法。获取 Condition 实例的监视器锁或者使用其监视器方法,与获取和该 Condition 相关的 Lock 或使用其 waiting 和 signalling 方法没有什么特定的关系。为了避免混淆,建议除了在其自身的实现中之外,切勿以这种方式使用 Condition 实例。

除非另行说明,否则为任何参数传递 null 值将导致抛出 NullPointerException。

实现注意事项
在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为 Condition 应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。

三种形式的条件等待(可中断、不可中断和超时)在一些平台上的实现以及它们的性能特征可能会有所不同。尤其是它可能很难提供这些特性和维护特定语义,比如排序保证。更进一步地说,中断线程实际挂起的能力在所有平台上并不是总是可行的。

因此,并不要求某个实现为所有三种形式的等待定义完全相同的保证或语义,也不要求其支持中断线程的实际挂起。

要求实现清楚地记录每个等待方法提供的语义和保证,在某个实现不支持中断线程的挂起时,它必须遵从此接口中定义的中断语义。

由于中断通常意味着取消,而又通常很少进行中断检查,因此实现可以先于普通方法的返回来对中断进行响应。即使出现在另一个操作后的中断可能会释放线程锁时也是如此。实现应记录此行为。

亮点

锁和监视器修饰词final 必须要用  锁不能修改


sleep和wait的区别?

1,sleep必须指定时间,wait可以指定可以不指定。
2,sleep和wait都可以让线程处于冻结状态,释放执行权。(相同点)
3,持有锁的线程执行sleep,不释放锁,持有锁的线程执行到wait释放锁。
4,sleep到时间会自动醒,wait没有指定时间,只能被其他线程通过notify唤醒。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值