以生产者和消费者为案例:生产者生产一件商品,消费者购买一件商品。
public class Product { private String brand; private String name; // setter,getter,toString }
public class ProducerThread extends Thread { // 共享商品 private Product p; public ProducerThread(Product product) { this.p = product; } @Override public void run() { for (int i = 1; i <= 10; i++) { if (i % 2 == 0) { p.setBrand("费列罗"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } p.setName("巧克力"); } else { p.setBrand("哈尔滨"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } p.setName("啤酒"); } System.out.println("生产者生产了:" + p.getBrand() + "---" + p.getName()); } } }
public class Cuntomer extends Thread { private Product p; public Cuntomer(Product p) { this.p = p; } @Override public void run() { for (int i = 1; i <= 10 ; i++) { System.out.println("消费者消费了:" + p.getBrand() + "---" + p.getName()); } } }
public static void main(String[] args) { Product p = new Product(); ProducerThread pt = new ProducerThread(p); Cuntomer c = new Cuntomer(p); pt.start(); c.start(); }
造成原因:当生产者生产“哈尔滨啤酒”的时候,先打印“哈尔滨”,线程sleep(100)期间,消费者把线程资源抢走,并打印出哈尔滨。但因为“啤酒”是要睡完100ms后才能打印出来,所以在产品名称处才会打印出null。当消费者执行完后,线程资源才重新落回生产者这里, 所以才能循环打印出哈尔滨啤酒和费列罗巧克力
改进:仍存在生产者和消费者无法交叉进行交易的问题
同步代码块
public class ProducerThread extends Thread { // 共享商品 private Product p; public ProducerThread(Product product) { this.p = product; } @Override public void run() { for (int i = 1; i <= 10; i++) { synchronized (p){ if (i % 2 == 0) { p.setBrand("费列罗"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } p.setName("巧克力"); } else { p.setBrand("哈尔滨"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } p.setName("啤酒"); } System.out.println("生产者生产了:" + p.getBrand() + "---" + p.getName()); } } } }
public class Cuntomer extends Thread { private Product p; public Cuntomer(Product p) { this.p = p; } @Override public void run() { for (int i = 1; i <= 10 ; i++) { synchronized (p){ System.out.println("消费者消费了:" + p.getBrand() + "---" + p.getName()); } } } }
同步方法:
package com.yzc.threads.thread04; public class Product { private String brand; private String name; // setter,getter,toString // 设置生产者生产商品的方法 public synchronized void setProduct(String brand,String name){ this.setBrand(brand); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } this.setName(name); System.out.println("生产者生产了:" + this.getBrand() + "---" + this.getName()); } // 设置消费者获取商品的方法 public synchronized void getProduct(){ System.out.println("消费者消费了:" + this.getBrand() + "---" + this.getName()); } }
public class ProducerThread extends Thread { // 共享商品 private Product p; public ProducerThread(Product product) { this.p = product; } @Override public void run() { for (int i = 1; i <= 10; i++) { if (i % 2 == 0) { p.setProduct("哈尔滨", "啤酒"); } else { p.setProduct("费列罗", "巧克力"); } } } }
public class Cuntomer extends Thread { private Product p; public Cuntomer(Product p) { this.p = p; } @Override public void run() { for (int i = 1; i <= 10 ; i++) { p.getProduct(); } } }
再次改进:解决生产者和交易者无法进行交叉交易的问题(使用wait()和notify())
public class Product { private String brand; private String name; boolean isExist = false;// ture:表示有商品;false:表示没有商品 // setter,getter,toString public synchronized void setProduct(String brand, String name) { if(isExist == true){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this.setBrand(brand); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } this.setName(name); System.out.println("生产者生产了:" + this.getBrand() + "---" + this.getName()); isExist = true; notify(); } public synchronized void getProduct() { if(isExist == false){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("消费者消费了:" + this.getBrand() + "---" + this.getName()); isExist = false; notify(); } }
ProducerThread和Cuntomer两个类代码不变,解决
其中wait()和notify():
注意:
这两个方法必须放在同步方法或者同步代码块中才生效,因为在同步的基础上进行线程通信才是有效的
wait和sleep的区别,sleep进入阻塞装填后没有释放锁,wait进入阻塞状态的同时会释放锁
更进一步:
如果生产者和消费者是多个的话,那么很有可能当生产者释放了锁后,另一个生产者抢到了资源,所以为了避免这个问题,可以将生产者和消费者放到两个不同的等待队列中
使用Lock和Condition类中的await(),signal(),signalAll
public class Product { private String brand; private String name; boolean isExist = false;// ture:表示有商品;false:表示没有商品 Lock lock = new ReentrantLock(); // 生产者等待队列 Condition productCondition = lock.newCondition(); // 消费者等待队列 Condition constomerCondition = lock.newCondition(); // setter,getter,toString public void setProduct(String brand, String name) { lock.lock(); try{ if (isExist == true) { try { productCondition.await();// 生产者阻塞,生产者进入等地队列中去 } catch (InterruptedException e) { e.printStackTrace(); } } this.setBrand(brand); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } this.setName(name); System.out.println("生产者生产了:" + this.getBrand() + "---" + this.getName()); isExist = true; // 通知消费者消费 constomerCondition.signal(); }finally { lock.unlock(); } } public void getProduct() { lock.lock(); try{ if (isExist == false) { try { constomerCondition.await(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("消费者消费了:" + this.getBrand() + "---" + this.getName()); isExist = false; // 通知生产者生产 productCondition.signal(); }finally { lock.unlock(); } } }
其余代码不变
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)