Java--多线程(三)

本文详细解析了经典的生产者和消费者问题,通过多线程通信实现了资源的有效管理和分配。介绍了如何利用Java的synchronized关键字及wait、notify等方法实现线程间的等待唤醒机制,进而避免资源竞争和死锁。此外,还探讨了Java 1.5引入的新特性Lock和Condition接口,以更加灵活的方式解决了多生产者多消费者场景下的问题。

 

经典问题:生产者和消费者问题详细解析--引出多线程通信和JDK1.5新特性。

按照之前的知识,可以将上面的问题写成:

 

package ThreadDemo;
import java.util.concurrent.locks.*;
/*
生产者  、消费者案例:
 */
class Resource2//创建一个共享资源:Resource2
{
	private String name;
	private int count=1;
	private boolean flag = false;

	public synchronized void set(String name)
	{
		this.name = name+count;
		count++;
		System.out.println(Thread.currentThread().getName()+"...生产者...生产"+this.name);
		flag = true;
	}
	public synchronized void out()
	{
		System.out.println(Thread.currentThread().getName()+"...消费者...消费"+this.name);
		flag = false;
	}
}
class Producer implements Runnable
{
	private Resource2 r;
	Producer(Resource2 r)
	{
		this.r =r;
	}
	public void run()
	{
		while(true)
		{
			r.set("烤鸭");
		}
	}
}
class Consumer implements Runnable
{
	private Resource2 r;
	Consumer(Resource2 r)
	{
		this.r =r;
	}
	public void run()
	{
		while(true)
		{
			r.out();
		}
	}
}
public class ProductAndConsumer {
	/**
	 * @param args
	 */
	public static void main(String[] args) {


		Resource2 r = new Resource2();
		
		Producer pro = new Producer(r);
		Consumer con = new Consumer(r);
		Thread t0 = new Thread(pro);
		Thread t1 = new Thread(pro);
		Thread t2 = new Thread(con);
		Thread t3 = new Thread(con);
		
		t0.start();
		t1.start();
		t2.start();
		t3.start();
		
	}
}

 

 

 

 

 

但是,不会出现生产消费循环:结果一个是生产,一个是消费。这时,就引出了等待唤醒机制:
等待唤醒的机制涉及的方法:
1、wait():让线程处于冻结状态,被wait的线程会存储到线程池当中。
线程池:存储线程的容器;
2、notify():用于唤醒线程池当中的一个线程(任意);
3、notifyAll():用于唤醒线程池当中的所有线程;
这些方法都必须定义在同步中,因为,这些方法是用于操作线程状态的方法。
必须要明确到底操作的是哪个锁上的线程。
添加后:

 

public synchronized void set(String name)
	{
		while/*if*/(flag)
			try{this.wait();}catch (InterruptedException e) {}
		this.name = name+count;
		count++;
		System.out.println(Thread.currentThread().getName()+"...生产者...生产"+this.name);
		flag = true;
		notifyAll();//由于,不知道是不是会唤醒到消费线程,所以,将所有在线程池中所有的线程唤醒;
	}
	public synchronized void out()
	{
		while/*if*/(!flag)
			try{this.wait();}catch (InterruptedException e) {}
		System.out.println(Thread.currentThread().getName()+"...消费者...消费"+this.name);
		flag = false;
		notifyAll();
	}

 

 

 

 

 

修改后,输出的结果:

这时,结果又可能会出现下面的这种状况:
Thread-0...生产者...生产烤鸭430286
Thread-2...消费者...消费烤鸭430286
Thread-3...消费者...消费烤鸭430286
Thread-0...生产者...生产烤鸭430287
Thread-2...消费者...消费烤鸭430287
Thread-3...消费者...消费烤鸭430287
多个消费者同时消费同一个产品,这是不允许的。

 

总结一下上面解决问题的方法:

第一、while判断标记,解决了线程获取执行权后是否要运行的问题。
第二、notifyAll解决了唤醒线程的问题,本方线程一定会唤醒对方线程。
 

首先将if判断标记改为while,因为if判断只有一次,会导致不该运行的线程运行了,出现了数据错误的情况。将if(flag)和if(!flag)改为while(flag)和while(flag)后,会解决掉上面的问题,但是,修改后会导致死锁的出现。同时,不能唤醒对方的线程。

如果使用notify,它只能唤醒一个线程,如果本方唤醒了本方,内有意义。而且while判断标记+notify会出现死锁问题。

如何解决这个问题?
在Java JDK1.5提供了新的解决形式:
public interface Lock
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,
可以具有差别很大的属性,可以支持多个相关的 Condition 对象。 
 对于同步代码块,对锁的操作是隐式的,而对于JDK1.5之后,使用Lock来对所进行对象封装。
 在JDK1.5以后,将同步和锁封装成了对象,并将操作锁的隐式方法定义到了对象当中,并将隐式动作变成了显示动作。
    Lock类中方法:
    lock()  获取锁;
    unlock()  释放锁;
    newCondition()  返回绑定到此 Lock 实例的新 Condition 实例。 
 如果在获取锁和释放锁包围中的代码中有异常,这时,就不会释放锁。锁不会释放,其他线程也不会进入。
 所以,必须将unlock()放到finally代码块当中。
 在JDK1.5后,将监视器封装成了condition对象。任意Lock对象可以和多组监视器对象进行组合。

 
Lock类的特点:它的出现替代了同步代码块或者同步函数。将同步的隐式锁操作变成现实锁操作。
 同时更为灵活。可以一个锁上加上多组监视器。
 lock():获取锁;
 unlock():释放锁。通常定义在finally代码块中。
 Condition接口:出现替代了Object类中的wait notify notifyAll方法。
          将这些监视器方法单独进行了封装,变成Condition监视器对象。 可以任意锁进行组合。
修改后的程序:

 

class Resource2
{
	private String name;
	private int count=1;
	private boolean flag = false;
	
	//创建新的锁对象;
		Lock lock = new ReentrantLock();
//通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者。
		Condition producer_con = lock.newCondition();
		Condition consumer_con = lock.newCondition();		
		public  void set(String name)
		{
			lock.lock();
			try{
					while(flag)
						try{producer_con.await();}catch (InterruptedException e) {}
					this.name = name+count;
					count++;
					System.out.println(Thread.currentThread().getName()+"...生产者...生产"+this.name);
					flag = true;
					consumer_con.signal();
				}
				finally{
					lock.unlock();
				}
		}
		
		public void out()
		{
			lock.lock();
			try{
					while(!flag)
						try{consumer_con.await();}catch (InterruptedException e) {}
					System.out.println(Thread.currentThread().getName()+"...消费者...消费"+this.name);
					flag = false;
					producer_con.signal();
				}finally{
					lock.unlock();
				}
		}
class Producer implements Runnable
{
	private Resource2 r;
	Producer(Resource2 r)
	{
		this.r =r;
	}
	public void run()
	{
		while(true)
		{
			r.set("烤鸭");
		}
	}
}
class Consumer implements Runnable
{
	private Resource2 r;
	Consumer(Resource2 r)
	{
		this.r =r;
	}
	public void run()
	{
		while(true)
		{
			r.out();
		}
	}
}
public class ProductAndConsumer {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Resource2 r = new Resource2();
		
		Producer pro = new Producer(r);
		Consumer con = new Consumer(r);
/*
 多生产、多消费者的问题。		
 */
		Thread t0 = new Thread(pro);
		Thread t1 = new Thread(pro);
		Thread t2 = new Thread(con);
		Thread t3 = new Thread(con);		
		t0.start();
		t1.start();
		t2.start();
		t3.start();		
	}
}

 

 

 

 

 

通过上面的实现,可以完成一开始的需求。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值