java多线程同步问题--生产者与消费者

线程的同步问题

当我们使用多线程对同一块数据进行操作的时候,会出现线程的同步,导致数据的错误,所以我们要对多线程的公共数据块上锁。

同步机制synchronized:synchronized关键字用于修饰方法或者单独的synchronized代码块,当一个线程想执行synchronized中的内容时,必须先获取到对象锁,当对象锁没有线程占用时,进入synchronized方法会自动获取到对象锁,执行完毕后会自动释放锁,如果对象锁被A线程占用,B线程想执行synchronized的代码只能等待A个线程执行完毕后, 释放对象锁,B线程才能获取到对象锁进入方法执行。一个线程获得对象A的锁,也可以获得对象B的锁,两个不同类的对象锁没有关联。如果把锁放在每个线程中 则每个线程执行方法时,都获取的是自己的锁,都可以获得许可,所以依旧会产生线程同步。

生产者消费者问题(Producer-consumer problem),也称有限缓冲问题(Bounded-buffer problem),是一个多线程同步问题的经典案例。生产者生成一定量的数据放到缓冲区中,然后重复此过程;与此同时,消费者也在缓冲区消耗这些数据。生产者和消费者之间必须保持同步,要保证生产者不会在缓冲区满时放入数据,消费者也不会在缓冲区空时消耗数据。不够完善的解决方法容易出现死锁的情况,此时进程都在等待唤醒。所以可用wait()和notifyAll()方法来等待、唤醒线程。
先举一个锁放在线程中错误的例子:
一个线程获得对象A的锁,也可以获得对象B的锁,两个不同类的对象锁没有关联。如果把锁放在每个线程中 则每个线程执行方法时,都获取的是自己的锁,都可以获得许可,所以依旧会产生线程同步。
Product:

public class Product {
	private String name;
	private int num;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getNum() {
		return num;
	}
	public void setNum(int num) {
		this.num = num;
	}
}

CThread(ConsumerThread):

public class CThread extends Thread{

	Product pro=new Product();
	public CThread(Product pro) {
		this.pro=pro;
	}
	public void run() {
		//消费者消费
		while(true) {
			synchronized(pro) {
				if(pro.getNum()<=0) {
					System.out.println(this.currentThread().getName()+"消费者访问,已经消费完了");
					try {
						pro.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
				pro.notifyAll();
				System.out.println(this.currentThread().getName()+"消费者消费1个");
				pro.setNum(pro.getNum()-1);
				System.out.println("当前数量为:"+pro.getNum());
			}
		}
	}
}
PThread(ProduceThread):
public class PThread extends Thread{

	Product pro=new Product();
	public PThread(Product pro) {
		this.pro=pro;
	}
	public void run() {
		//生产者生产
		synchronized(pro) {
		while(true) {
			synchronized(pro) {
				if(pro.getNum()>=5) {
					System.out.println(this.currentThread().getName()+"生产者访问,已经满了");
					try {
						pro.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
				pro.notifyAll();
				System.out.println(this.currentThread().getName()+"生产者生产1个");
				pro.setNum(pro.getNum()+1);
				System.out.println("当前数量为:"+pro.getNum());
			}
		}
		}
	}
}

Test:

public class test {
	public static void main(String args[]) {
	Product pro=new Product();
	pro.setNum(4);

	PThread p=new PThread(pro);
	CThread c=new CThread(pro);
	Thread p1=new Thread(p,"p1");
	Thread p2=new Thread(p,"p2");
	Thread p3=new Thread(p,"p3");
	Thread c1=new Thread(c,"c1");
	Thread c2=new Thread(c,"c2");
	p1.start();
	p2.start();
	p3.start();
	c1.start();
	c2.start();
	}
}

以上代码虽然是有锁了,但是锁在了线程中,测试时,每个线程都会实例化一个自己的锁,所以每次申请的都是自己的锁,没有其他的线程争夺,所以依旧是会出现线程同步问题。
正确代码:

public class Consumer extends Thread{
	private Product pro;
	public Consumer(Product pro) {
		this.pro=pro;
	}
	public void run() {
		
			while(true) {
			try {
				Thread.sleep(3000);
				pro.consumer();
			} catch (InterruptedException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
			}
		
	}
}
public class Producer extends Thread{
	private Product pro;
	public Producer(Product pro) {
		this.pro=pro;
	}
	public void run() {
		
			while(true) {
			try {
				Thread.sleep(1000);
				pro.produce();
			} catch (InterruptedException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
			}
		
	}
}
public class Product {
	private LinkedList data=new LinkedList();
	public void produce() throws InterruptedException{
		synchronized(data) {
			if(data.size()==5) {
				System.out.println("已经满了");
				data.wait();
			}
			data.add(new Object());
			System.out.println(Thread.currentThread().getName()+"生产一个,现在有"+data.size()+"个");
			data.notifyAll();
	}
	}
	public void consumer() throws InterruptedException{
		synchronized(data) {
			if(data.size()==0) {
				System.out.println("已经没有了");
				data.wait();
			}
			data.remove();
			System.out.println(Thread.currentThread().getName()+"消费一个现在还有"+data.size()+"个");
			data.notifyAll();
		}
	}
}
public class Test {
	public static void main(String args[]) {
		Product pro=new Product();
		Producer p=new Producer(pro);
		Consumer c=new Consumer(pro);
		Producer p1=new Producer(pro);
		Consumer c1=new Consumer(pro);
		p.start();
		p1.start();
		c.start();
		c1.start();
	}
}

这样每个线程访问的就是同一个资源了,当多个线程同时访问时,会只有一个线程进去,然后其他线程进行等待。

如果各位有兴趣的话,可以去看看这个帖子,这为作者还提供了了其他的方法
Java多种方式解决生产者消费者问题(十分详细)------Xiyou_limeng

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值