java多线程实现生产者消费者问题

wait/notify机制实现原理

在使用wait/notify方法前我们需要对这两个方法进行简单的介绍:
使用wait/notify方法前提是拥有相同锁的线程才可以实现此机制。

1)wait方法是object的方法,他的作用是使当前执行wait方法的线程等待,在wait方法后面的代码暂停执行,并且释放锁,直到接到通知或被中断为止。在调用wait方法前,线程必须获得该对象的对象级别的锁,即只能在同步方法或同步代码块中调用wait方法。如果调用wait方法时没有适当的锁会抛出IllegalMonitorStateException,它是RuntimeException的一个子类,因此不需要try catch捕获异常。

2)notify方法也是要在同步方法或同步代码块中调用,即在调用方法前线程必须获得锁,如果调用notify时没有持有适当的锁,则会抛出IllegalMonitorStateException。该方法的作用时用来通知那些在等待该锁的其他线程,如果有多个线程等待,则按照执行wait方法的顺序对处于wait状态的线程发出一次通知(notify),并使该线程重新获得锁,不过当前线程并不会立即释放锁,呈wait状态的线程也并不会马上获得该对象的锁,而是要等到执行notify方法的线程将程序执行完,也就是退出synchronized同步区域后,该线程才会释放锁,呈wait状态的线程才可以获得锁。

wait/notify机制解决生产者消费者问题

产品类:

public class Goods {
     
    private String brand;//产品品牌
    private String name;//产品名称

    public String getBrand() {
     
        return brand;
    }

    public void setBrand(String brand) {
     
        this.brand = brand;
    }

    public String getName() {
     
        return name;
    }

    public void setName(String name) {
     
        this.name = name;
    }

    @Override
    public String toString() {
     
        return "Goods{" +
                "brand='" + brand + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

业务类:

public class Service {
     
    private Goods goods;
    private boolean flag=false;

    public Service(Goods goods) {
     
        this.goods = goods;
    }

    public synchronized void setGoods(){
     
        if(flag) {
     
            //有商品生产者等待
            try {
     
                 super.wait();
            } catch (Exception e) {
     
                e.printStackTrace();
            }
        }
        try {
     
            goods.setBrand("康师傅");
            Thread.sleep(500);
            goods.setName("红烧牛肉面");
            System.out.println("生产者生产了"+goods.getBrand()+goods.getName());
            flag=true;//生产完产品
            super.notify();//通知消费者来取
        } catch (InterruptedException e) {
     
            e.printStackTrace();
        }
    }
    public synchronized void getGoods(){
     
        if(!flag) {
     
            //没有商品消费者等待
            try {
     
                super.wait();
            } catch (Exception e) {
     
                e.printStackTrace();
            }
        }
        try {
     
            Thread.sleep(500);
            System.out.println("消费者取走了"+goods.getBrand()+goods.getName());
            flag=false;//取走产品
            super.notify();//通知生产者生产
        } catch (InterruptedException e) {
     
            e.printStackTrace();
        }
    }
}

生产者:

public class Producter implements Runnable{
     
    //生产者
    private Service service;

    public Producter(Service service) {
     
        this.service = service;
    }

    @Override
    public void run() {
     
        for (int i = 0; i < 5; i++) {
     
            service.setGoods();
        }
    }
}

消费者:

public class Consumer implements Runnable{
     
    //生产者
    private Service service;

    public Consumer(Service service) {
     
        this.service = service;
    }

    @Override
    public void run() {
     
        for (int i = 0; i < 5; i++) {
     
            service.getGoods();
        }
    }
}

测试类:

public class Test {
     
    public static void main(String[] args) {
     
        Goods goods = new Goods();//产品
        Service service = new Service(goods);
        Producter producter = new Producter(service);//生产者
        Consumer consumer = new Consumer(service);//消费者
        new Thread(producter).start();
        new Thread(consumer).start();
    }
}
结果:
生产者生产了康师傅红烧牛肉面
消费者取走了康师傅红烧牛肉面
生产者生产了康师傅红烧牛肉面
消费者取走了康师傅红烧牛肉面
生产者生产了康师傅红烧牛肉面
消费者取走了康师傅红烧牛肉面
生产者生产了康师傅红烧牛肉面
消费者取走了康师傅红烧牛肉面
生产者生产了康师傅红烧牛肉面
消费者取走了康师傅红烧牛肉面

多个生产者消费者带来的问题

public class Test {
     
    public static void main(String[] args) {
     
        Goods goods = new Goods();//产品
        com.Service service = new Service(goods);
        Thread product1= new Thread(new Producter(service));
        product1.setName("生产者1");
        product1.start();
        Thread product2 = new Thread(new Producter(service));
        product2.setName("生产者2");
        product2.start();
        Thread consumer1 = new Thread(new Consumer(service));
        consumer1.setName("消费者1");
        consumer1.start();
        Thread consumer2 = new Thread(new Consumer(service));
        consumer2.setName("消费者2");
        consumer2.start();

    }
}
结果:
生产者1生产者生产了康师傅红烧牛肉面
**消费者1消费者取走了康师傅红烧牛肉面**
生产者2生产者生产了康师傅红烧牛肉面
**消费者1消费者取走了康师傅红烧牛肉面
**消费者2消费者取走了康师傅红烧牛肉面
生产者2生产者生产了康师傅红烧牛肉面
**消费者1消费者取走了康师傅红烧牛肉面
**消费者2消费者取走了康师傅红烧牛肉面
生产者2生产者生产了康师傅红烧牛肉面
**消费者1消费者取走了康师傅红烧牛肉面
**消费者2消费者取走了康师傅红烧牛肉面
生产者2生产者生产了康师傅红烧牛肉面
**消费者1消费者取走了康师傅红烧牛肉面
**消费者2消费者取走了康师傅红烧牛肉面
生产者2生产者生产了康师傅红烧牛肉面
**消费者2消费者取走了康师傅红烧牛肉面
生产者1生产者生产了康师傅红烧牛肉面

从运行结果来看分析可知有的线程呈现了"假死现象"即一直在waiting状态,不执行任务了。虽然使用了wait/notify通信但是不能保证notify唤醒的是同类,即消费者唤醒消费者,生产者唤醒生产者。

使用wait/notifyall机制解决多个线程“假死问题”

将业务类的notify方法全部换成notifyall方法即可。

public class Servicer {
     
    private Goods goods;
    private boolean flag=false;

    public Servicer(Goods goods) {
     
        this.goods = goods;
    }

    public synchronized void setGoods(){
     
        if(flag) {
     
            //有商品生产者等待
            try {
     
                 super.wait();
            } catch (Exception e) {
     
                e.printStackTrace();
            }
        }
        try {
     
            goods.setBrand("康师傅");
            Thread.sleep(500);
            goods.setName("红烧牛肉面");
            System.out.println(Thread.currentThread().getName()+"生产了"+goods.getBrand()+goods.getName());
            flag=true;//生产完产品
            super.notifyAll();//通知消费者来取
        } catch (InterruptedException e) {
     
            e.printStackTrace();
        }
    }
    public synchronized void getGoods(){
     
        if(!flag) {
     
            //没有商品消费者等待
            try {
     
                super.wait();
            } catch (Exception e) {
     
                e.printStackTrace();
            }
        }
        try {
     
            Thread.sleep(500);
            System.out.println(Thread.currentThread().getName()+"取走了"+goods.getBrand()+goods.getName());
            flag=false;//取走产品
            super.notifyAll();//通知生产者生产
        } catch (InterruptedException e) {
     
            e.printStackTrace();
        }
    }
public class Tests {
     
    public static void main(String[] args) {
     
        Goods goods = new Goods();//产品
        Servicer service = new Servicer(goods);
        Thread product1= new Thread(new Producter(service));
        product1.setName("生产者1");
        product1.start();
        Thread product2 = new Thread(new Producter(service));
        product2.setName("生产者2");
        product2.start();
        Thread consumer1 = new Thread(new Consumer(service));
        consumer1.setName("消费者1");
        consumer1.start();
        Thread consumer2 = new Thread(new Consumer(service));
        consumer2.setName("消费者2");
        consumer2.start();

    }
}
结果:
生产者1生产了康师傅红烧牛肉面
消费者2取走了康师傅红烧牛肉面
生产者2生产了康师傅红烧牛肉面
消费者2取走了康师傅红烧牛肉面
生产者2生产了康师傅红烧牛肉面
消费者2取走了康师傅红烧牛肉面
生产者2生产了康师傅红烧牛肉面
消费者2取走了康师傅红烧牛肉面
生产者2生产了康师傅红烧牛肉面
消费者1取走了康师傅红烧牛肉面
生产者1生产了康师傅红烧牛肉面
消费者1取走了康师傅红烧牛肉面
生产者1生产了康师傅红烧牛肉面
消费者1取走了康师傅红烧牛肉面
生产者1生产了康师傅红烧牛肉面
消费者1取走了康师傅红烧牛肉面

await/signal机制解决生产者消费者问题

关键字synchronized与wait(),notify(),notifyall()方法相结和可以实现wait/notify模式,ReentrantLock类也可以实现同样的功能,但是需要借助Condition对象,condition类是JDK5的技术,具有更好的灵活性,可以实现多路通知功能,也就是在一个Lock对象中可以创建多个condition实例,线程对象在指定的condition中可以有选择性的进行线程通知,在调度上更加灵活。

在使用await/signal机制解决生产者消费者问题前我们需要先了解一下ReentrantLock类的作用,该类是JDK1.5新增的类,能够达到和synchronized关键字同样的效果,并且在功能上更加强大,如具有嗅锁探定(可以通过tryLock方法进行嗅探拿锁,如果当前线程需要的锁被其他线程持有则返回false)多路分支通知(也就是在一个Lock对象中可以创建多个condition实例,线程对象在指定的condition中可以有选择性的进行线程通知,在调度上更加灵活),公平非公平锁(公平锁采用先到先得的策略,每次锁之前会检查队列里有没有排队等待的线程,如果有就将当前线程加到队列中,如果没有就会获得锁。而非公平锁是可以插队的可以后来但是能先得到锁)
下面我们用await/signal机制来解决生产者消费者问题:
业务类:

public class Service {
     
    private Goods goods;
    private boolean flag=false;
    private Lock lock=new ReentrantLock();
    Condition condition=lock.newCondition();

    public Service(Goods goods) {
     
        this.goods = goods;
    }

    public  void setGoods(){
     
        lock.lock();
        if(flag) {
     
            //有商品生产者等待
            try {
     
                 condition.await();
            } catch (Exception e) {
     
                e.printStackTrace();
            }
        }
        try {
     
            goods.setBrand("康师傅");
            Thread.sleep(500);
            goods.setName("红烧牛肉面");
            System.out.println("生产者生产了"+goods.getBrand()+goods.getName());
            flag=true;//生产完产品
           condition.signal();//通知消费者来取
        } catch (InterruptedException e) {
     
            e.printStackTrace();
        }finally {
     
            lock.unlock();
        }
    }
    public void getGoods(){
     
        lock.lock();
        if(!flag) {
     
            //没有商品消费者等待
            try {
     
                condition.await();
            } catch (Exception e) {
     
                e.printStackTrace();
            }
        }
        try {
     
            Thread.sleep(500);
            System.out.println("消费者取走了"+goods.getBrand()+goods.getName());
            flag=false;//取走产品
            condition.signal();//通知生产者生产
        } catch (InterruptedException e) {
     
            e.printStackTrace();
        }finally {
     
            lock.unlock();
        }
    }
}

测试类:

public class Test {
     
    public static void main(String[] args) {
     
        Goods goods = new Goods();//产品
        Service service = new Service(goods);
        Producter producter = new Producter(service);//生产者
        Consumer consumer = new Consumer(service);//消费者
        new Thread(producter).start();
        new Thread(consumer).start();
    }
}
结果:
生产者生产了康师傅红烧牛肉面
消费者取走了康师傅红烧牛肉面
生产者生产了康师傅红烧牛肉面
消费者取走了康师傅红烧牛肉面
生产者生产了康师傅红烧牛肉面
消费者取走了康师傅红烧牛肉面
生产者生产了康师傅红烧牛肉面
消费者取走了康师傅红烧牛肉面
生产者生产了康师傅红烧牛肉面
消费者取走了康师傅红烧牛肉面

使用await/signalAll机制解决多个线程“假死问题”

业务类:
我们同样也可以将上面signal方法改成signalAll方法来解决多个线程“假死问题”

public class Service {
     
    private Goods goods;
    private boolean flag=false;
    private Lock lock=new ReentrantLock();
    Condition condition=lock.newCondition();

    public Service(Goods goods) {
     
        this.goods = goods;
    }

    public  void setGoods(){
     
        lock.lock();
        if(flag) {
     
            //有商品生产者等待
            try {
     
                 condition.await();
            } catch (Exception e) {
     
                e.printStackTrace();
            }
        }
        try {
     
            goods.setBrand("康师傅");
            Thread.sleep(500);
            goods.setName("红烧牛肉面");
            System.out.println(Thread.currentThread().getName()+"生产了"+goods.getBrand()+goods.getName());
            flag=true;//生产完产品
           condition.signalAll();//通知消费者来取
        } catch (InterruptedException e) {
     
            e.printStackTrace();
        }finally {
     
            lock.unlock();
        }
    }
    public void getGoods(){
     
        lock.lock();
        if(!flag) {
     
            //没有商品消费者等待
            try {
     
                condition.await();
            } catch (Exception e) {
     
                e.printStackTrace();
            }
        }
        try {
     
            Thread.sleep(500);
            System.out.println(Thread.currentThread().getName()+"取走了"+goods.getBrand()+goods.getName());
            flag=false;//取走产品
            condition.signalAll();//通知生产者生产
        } catch (InterruptedException e) {
     
            e.printStackTrace();
        }finally {
     
            lock.unlock();
        }
    }
}

测试类:

public class Test {
     
    public static void main(String[] args) {
     
        Goods goods = new Goods();//产品
        Service service = new Service(goods);
        Thread product1= new Thread(new Producter(service));
        product1.setName("生产者1");
        product1.start();
        Thread product2 = new Thread(new Producter(service));
        product2.setName("生产者2");
        product2.start();
        Thread consumer1 = new Thread(new Consumer(service));
        consumer1.setName("消费者1");
        consumer1.start();
        Thread consumer2 = new Thread(new Consumer(service));
        consumer2.setName("消费者2");
        consumer2.start();
    }
}
结果:
生产者2生产了康师傅红烧牛肉面
消费者2取走了康师傅红烧牛肉面
生产者2生产了康师傅红烧牛肉面
生产者1生产了康师傅红烧牛肉面
消费者2取走了康师傅红烧牛肉面
消费者1取走了康师傅红烧牛肉面
生产者2生产了康师傅红烧牛肉面
生产者1生产了康师傅红烧牛肉面
消费者2取走了康师傅红烧牛肉面
消费者1取走了康师傅红烧牛肉面
生产者2生产了康师傅红烧牛肉面
生产者1生产了康师傅红烧牛肉面
消费者2取走了康师傅红烧牛肉面
消费者1取走了康师傅红烧牛肉面
生产者2生产了康师傅红烧牛肉面
生产者1生产了康师傅红烧牛肉面
消费者2取走了康师傅红烧牛肉面
消费者1取走了康师傅红烧牛肉面
生产者1生产了康师傅红烧牛肉面
消费者1取走了康师傅红烧牛肉面

你可能感兴趣的:(多线程,java,多线程,并发编程)