java并发(等待/通知机制)

来源:互联网 发布:sql一行多列 sum 求和 编辑:程序博客网 时间:2024/06/05 11:19

生产者与消费者的java实现及注意事项

       生产者消费者问题是研究多线程绕不开的经典问题,主要是有一块缓冲区作为仓储,生产者向里面生产资源,消费者消费其中的资源,一旦资源为空,则消费者通知生产者生产资源,一旦仓储中有资源,则生产者通知消费者消费资源。解决生产者消费者问题主要有两种思想:(1)采用信号或锁机制使生产者与消费者之间同步。(2)在生产者与消费者之间建立一个管道。采用同步的方法主要包括:
       (1)wait/notify机制
       (2)await/signal机制
       (3)LinkedBlockingQueue阻塞队列机制

一.采用wait/notify机制
       wait方法是指当前执行代码的线程进行等待,将当前线程置入“预执行队列”,直到调用notify方法或者中断线程位置,在调用wait方法之前,必须获得该对象的对象锁,即wait方法必须在同步方法或同步代码块中使用,调用wait方法或,线程会释放对象锁,当调用notify(notifyall)方法后,与其他线程重新竞争获得对象锁。wait()方法的调用不需要捕捉异常。
       notify()方法同wait()方法一样是Object类的方法,也需要在同步方法或者同步代码块中执行,代用该方法之前也需要获得给对象的锁,调用该方法后,会通知可能等待该对象锁的其他进程,如果有多个线程等待该对象锁,会随机挑选一个wait的线程通知其notify,当调用notify方法后,wait的并不会立即获取对象锁,只有当notify所在的同步代码块执行完后,wait的线程才会来获取对象锁。wait和notify方法是一对一对应的,一次notify只能唤醒一个wait。

a.一生产者与一消费者

生产者:public class Producer extends Thread{    private String lock;//存储对象锁    Producer(String lock){        this.lock = lock;    }    public void run(){        while(true){            try{                synchronized(lock){                    if(!Value.value.equals("")){//值如果改变,则通知消费者消费,生产者等待,否则改变存储值                        lock.wait();                    }                    String value = System.currentTimeMillis()+Thread.currentThread().getName();                    Value.value = value;                    System.out.println("设置的value值是"+value);                    lock.notify();                    Thread.sleep(1000); //设置睡眠时间                }            }catch(Exception e){                e.printStackTrace();            }        }    }}消费者:package com.org.wh.onetoone;import java.util.LinkedList;public class Consumer extends Thread{    private String lock;//存储对象锁    Consumer(String lock){        super();        this.lock = lock;    }    public void run(){        while(true){            try{                synchronized(lock){                    if(Value.value.equals("")){//存储值是否为“”,是则消费者等待,通知生产者生产,否则消费者消费同时将存储值恢复                        lock.wait();                    }                    System.out.println("get的value值"+Value.value);                    Value.value = "";                    lock.notify();                    Thread.sleep(1000);//设置睡眠时间                }            }catch(Exception e){                e.printStackTrace();            }        }    }}存储值的java代码:public class Value {     //不能为private     public static String value="";}Main函数:import java.util.LinkedList;public class Main {    public static void main(String[] args) {        String lock = Value.value;//存储对象        Producer p = new Producer(lock);        Consumer c = new Consumer(lock);        p.start();        c.start();    }}

b.多生产者与多消费者

生产者:import java.util.LinkedList;public class Producer extends Thread{    private LinkedList<String> lock;//存储对象    Producer(LinkedList<String> lock){        this.lock = lock;    }    @Override    public void run() {        while(true){            try{                synchronized(lock){                    if(Storage.list.size()==Storage.Max){//判断存储是否满,是则生产者等待,否则生产者生产同时通知消费者消费                        lock.wait();                        System.out.println(Thread.currentThread().getState());                    }                    System.out.println("存储数据"+System.currentTimeMillis()+Thread.currentThread().getName()+"   "+Storage.list.size());                    Storage.list.add(System.currentTimeMillis()+Thread.currentThread().getName());                    lock.notify();                    Thread.sleep(100);                }            }catch(Exception e){                e.printStackTrace();            }        }    }}消费者:import java.util.LinkedList;public class Consumer extends Thread {    private LinkedList<String> lock;//存储对象    Consumer(LinkedList<String> lock){        this.lock = lock;    }    @Override    public void run() {        while(true){            try{                synchronized(lock){                    if(Storage.list.size()==0){//判断存储是否为空,是则通知生产者生产,消费者等待                        lock.wait();                        System.out.println(Thread.currentThread().getState()+"   "+1);                    }                    System.out.println("获取数据"+Storage.list.remove()+"    "+Storage.list.size());                    lock.notify();                    Thread.sleep(100);                }            }catch(Exception e){            }        }    }}存储:import java.util.LinkedList;public class Storage {    public static LinkedList<String> list = new LinkedList<>();    public static int Max = 40;}主线程:package com.org.wh.duotoduo;import java.util.LinkedList;public class Main {    public static void main(String[] args) {        LinkedList<String> lock = Storage.list;//获取存储对象        Producer p = new Producer(lock);        p.start();        Producer p1 = new Producer(lock);        p1.start();        Producer p2 = new Producer(lock);        p2.start();        Producer p3 = new Producer(lock);        p3.start();        Producer p4 = new Producer(lock);        p4.start();        Consumer c = new Consumer(lock);        c.start();        Consumer c1 = new Consumer(lock);        c1.start();        Consumer c2 = new Consumer(lock);        c2.start();        Consumer c3 = new Consumer(lock);        c3.start();        Consumer c4 = new Consumer(lock);        c4.start();    }}

       注意:多消费者与多生产者模式中会出现如下的结果:
这里写图片描述这里写图片描述
原因:可能是在notify唤醒的是异类,而非同类,久而久之可能会造成假死状态,可以使用notifyall方法来解决。

二.阻塞队列实现

       java.util.concurrent包是在jdk1.5新引进的java并发包,对java多线程进行并发处理设计,尽量避免使用synchronized,提供并发性的可能。阻塞队列是在java并发包中提供的,主要包含以下几种:
       (1)ArrayBlockingQueue:一种基于数组实现的阻塞队列,具有先进先出性质,在创建时必须指定大小。
       (2)LinkedBlockingQueue :一种基于链表实现的阻塞队列,具有先进先出性质,在创建时不指定大小时,默认长度为Integer.MAX_VALUE。
       (3)PriorityBlockingQueue : 一种基于优先级的阻塞队列,优先级高的先出队列,低的后出队列,同时该队列是一个无界队列(前两个都有界)。
       (4)DelayQueue : 基于PriorityBlockingQueue实现的队列,具有延时性,只有到达时间限制后才能从队列中取出,该队列是一个无界队列,向队列中存入元素不会阻塞,但消费元素会有延迟(阻塞)。

阻塞队列与非阻塞队列包含方法的比较:
       (1)非阻塞队列具有add,remove,poll,offer,peek等方法,add与remove方法在插入成功时返回true,否则抛出异常,而poll,offer,peek方法在操作失败时返回false,所以在使用非阻塞队列时建议使用poll,offer,peek方法,会返回结果。
       (2)阻塞队列包含put,offer,poll,take方法。put,take方法在操作成功时返回true,否则等待;offer,poll方法在操作成功时返回true,否则等待一定时间,一定时间后还是无法操作返回false。

代码:

生产者:public class Producer extends Thread {    public void run() {        while(true){            try{                if(Storage.block.size()<Storage.Max){//队列为满时生产                    Storage.block.put("1");                    System.out.println("当前存放的数量"+Storage.block.size());                    Thread.sleep(1000);                }            }catch(Exception e){                e.printStackTrace();            }        }    }}消费者:public class Consumer extends Thread {    @Override    public void run() {        while(true){            try{                Storage.block.take();                System.out.println("当前存放的数量"+Storage.block.size());                Thread.sleep(1000);            }catch(Exception e){                e.printStackTrace();            }        }    }}
0 0