多线程安全--生产者和消费者

本文探讨多线程安全场景下的生产者消费者模型,重点讲解如何利用Java的synchronized和Lock(如ReentrantLock与Condition)确保线程同步。通过对新旧特性对比,阐述Lock接口在显式加解锁以及多条件变量上的优势。

    在多线程安全中,使用较多的例子是生产者和消费者模型。

    在这个模型中,生产者和消费者是两个独立的线程,但是他们操作的资源的共享的,生产者生产产品,消费者消费产品。假设资源用一个对象来抽象,该对象的set()和out()方法对应生产和消费,那么为了保证多线程安全,每次只能有一个生产者线程生产产品,即只有一个生产者线程可以调用set()函数,同样,每次只能有一个消费者消费产品,即只有一个消费者线程能调用out()函数,所以要加入同步机制,使用synchronized构造同步函数,或者使用jdk1.5之后的新特性,使用java.util.concurrent.locks包中的ReentrantLock和Condition来实现。

    Lock和Condition都是接口,Lock接口的方法比如lock()和unlock()代替了synchronized,Condition代替了Object监视器方法的使用。Condition子类对象是通过Lock的newCondiiton()产生的。Condition中有await()、signal()、signalAll()方法,分别代替Object中的wait()、notify()、notifyAll()。

    新旧特性对比:

    1)Lock的lock()和unlock()可以显式地加锁和解锁,而synchronized关键字并不能显式的加解锁。

    2)旧特性中,notify()和wait()必须对应一个锁才能进行唤醒操作,synchronized只对应一个锁,但是新特性中,一个Lock可以对应多个锁,可以创建多个监视器,每个监视器都可以进行线程的等待和唤醒操作,更灵活。

代码:

/*
 * 实现消费者和生产者模式
 * */
import java.util.concurrent.locks.*;

//共享资源,即商品
class Resource{
	private String name;//商品名
	private int count;//商品的个数
	boolean flag;//代表是否有商品,如果是true,才可以消费,如果是false,才可以生产

	Resource(String name){
		this.name = name;
	}

	/*
	 //这里使用的是jdk的旧特性
	//生产商品
	//由于商品是生产和消费两个线程的共享数据,所以需要同步才能避免多线程安全问题,所以对于生产和消费过程,每次都只能有一个线程参与,所以将生产和消费过程都进行同步处理
	public synchronized void set(String name)throws Exception{
		while(flag){
			try{
				wait();//该线程阻塞等待,会抛出异常InterruptedException
			}
			catch(Exception e){

			}
		}

		this.name = name+"----------"+(++count);
		System.out.println(Thread.currentThread().getName()+"生产::"+this.name);
		flag = true;
		notifyAll();//唤醒消费者线程,并且防止所有线程都处于等待状态
	}

	//消费商品
	//同生产商品线程,每次只能有一个线程进行消费,否则会出现冲突,因此消费过程也需要进行同步处理
	public synchronized void out()throws Exception{
		while(!flag){
			try{
				wait();
			}
			catch(Exception e){

			}
		}

		System.out.println(Thread.currentThread().getName()+"消费::::"+name);
		flag = false;
		notifyAll();//唤醒生产者线程,并且防止所有线程都处于等待状态
	}
	*/

	//使用jdk1.5之后的新特性
	//java.uti.concurrent.locks包中,有新特性下的多线程操作
	//新特性下,Lock的lock()和unlock()方法代替了synchronized方法
	//由Condition接口对象调用await()、signal()、signalAll(),代替锁(对象)调用的wait()、notify()、notifyAll()
	private ReentrantLock lock = new ReentrantLock();
	private Condition con_pro = lock.newCondition();//监视器,监视生产者
	private Condition con_con = lock.newCondition();//监视器,监视消费者

	//生产商品
	public void set(String name)throws InterruptedException{
		//加锁
		lock.lock();

		try{
			while(flag){
				//有产品,此时不能生产
				con_pro.await();//会抛异常InterruptedException
			}

			this.name = name+"------"+(++count);
			System.out.println(Thread.currentThread().getName()+"生产::"+this.name);
			flag = true;//此时有商品
			con_con.signal();//唤醒消费者线程
		}
		finally{
			//关闭资源
			lock.unlock();
		}
	}
	
	//消费商品
	public void out()throws InterruptedException{
		//加锁
		lock.lock();

		try{
			while(!flag){
				con_con.await();//此时没有商品,所以消费者等待
			}

			System.out.println(Thread.currentThread().getName()+"消费:::::"+name);
			flag = false;
			con_pro.signal();//唤醒生产者
		}
		finally{
			lock.unlock();//释放资源
		}
	}
}

//生产者线程
class ProducerThread implements Runnable{
	//生产者处理的资源
	private Resource res;

	ProducerThread(Resource res){
		this.res = res;
	}

	public void run(){
		try{
			while(true){
				res.set("商品");
			}
		}
		catch(Exception e){

		}
	}
}

//消费者线程
class ConsumerThread implements Runnable{
	//消费者消费的资源
	private Resource res;

	ConsumerThread(Resource res){
		this.res = res;
	}

	public void run(){
		try{
			while(true){
				res.out();
			}
		}
		catch(Exception e){

		}
	}
}

class ProCon{
	public static void main(String[] args){
		//创建共享资源
		Resource res = new Resource("商品");

		//创建生产者和消费者线程
		ProducerThread p1 = new ProducerThread(res);
		ProducerThread p2 = new ProducerThread(res);

		ConsumerThread c1 = new ConsumerThread(res);
		ConsumerThread c2 = new ConsumerThread(res);

		//开启线程并进行生产和消费的交替过程
		new Thread(p1).start();
		new Thread(p2).start();
		new Thread(c1).start();
		new Thread(c2).start();
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值