实现生产者消费者有几种方式?能列出来吗?

实现一个生产者——消费者模型(5种方式) 用五种方式来实现生产者消费者模型 Object.wait/notify Lock/Condition BlockingQueue Semaphore Exchanger 方式一:synchronized、wait和notify 创建一个资源类Resource: public class Resource { //当前资源量 int num = 0; //资源池中允许放的资源数目 int s... 阅读详情

推荐阅读:

生产者-消费者模式是一个十分经典的多线程并发协作的模式,弄懂生产者-消费者问题能够让我们对并发编程的理解加深。所谓生产者-消费者问题,实际上主要是包含了两类线程,一种是生产者线程用于生产数据,另一种是消费者线程用于消费数据,为了解耦生产者和消费者的关系,通常会有一个共享的数据区域,就像是一个仓库,生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为;而消费者只需要从共享数据区中去获取数据,就不再需要关心生产者的行为。但是,这个共享数据区域中应该具备这样的线程间并发协作的功能:

  1. 如果共享数据区已满的话,阻塞生产者继续生产数据放置入内;
  2. 如果共享数据区为空的话,阻塞消费者继续消费数据;

在实现生产者消费者问题时,可以采用三种方式:

  • 使用Object的wait/notify的消息通知机制;

  • 使用Lock的Condition的await/signal的消息通知机制;

  • 使用BlockingQueue实现。

本文主要将这三种实现方式进行总结归纳。

wait/notify的消息通知机制

预备知识

Java 中,可以通过配合调用 Object 对象的 wait() 方法和 notify()方法或 notifyAll() 方法来实现线程间的通信。在线程中调用 wait() 方法,将阻塞当前线程,直至等到其他线程调用了 notify() 方法或 notifyAll() 方法进行通知之后,当前线程才能从wait()方法出返回,继续执行下面的操作。

  1. wait

    该方法用来将当前线程置入休眠状态,直到接到通知或被中断为止。在调用 wait()之前,线程必须要获得该对象的对象监视器锁,即只能在同步方法或同步块中调用 wait()方法。调用wait()方法之后,当前线程会释放锁。如果调用wait()方法时,线程并未获取到锁的话,则会抛出IllegalMonitorStateException异常,这是一个RuntimeException。如果再次获取到锁的话,当前线程才能从wait()方法处成功返回。

  2. notify

    该方法也要在同步方法或同步块中调用,即在调用前,线程也必须要获得该对象的对象级别锁,如果调用 notify()时没有持有适当的锁,也会抛出 IllegalMonitorStateException。
    该方法任意从WAITTING状态的线程中挑选一个进行通知,使得调用wait()方法的线程从等待队列移入到同步队列中,等待有机会再一次获取到锁,从而使得调用wait()方法的线程能够从wait()方法处退出。调用notify后,当前线程不会马上释放该对象锁,要等到程序退出同步块后,当前线程才会释放锁。

  3. notifyAll
    该方法与 notify ()方法的工作方式相同,重要的一点差异是:
    notifyAll 使所有原来在该对象上 wait 的线程统统退出WAITTING状态,使得他们全部从等待队列中移入到同步队列中去,等待下一次能够有机会获取到对象监视器锁。

wait/notify消息通知潜在的一些问题

notify过早通知

notify 通知的遗漏很容易理解,即 threadA 还没开始 wait 的时候,threadB 已经 notify 了,这样,threadB 通知是没有任何响应的,当 threadB 退出 synchronized 代码块后,threadA 再开始 wait,便会一直阻塞等待,直到被别的线程打断。比如在下面的示例代码中,就模拟出notify早期通知带来的问题:

public class EarlyNotifyDemo1 {

    private static String lockObject = "";

    public static void main(String[] args) {
        WaitThread waitThread = new WaitThread(lockObject);
        NotifyThread notifyThread = new NotifyThread(lockObject);
        notifyThread.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        waitThread.start();
    }

    static class WaitThread extends Thread {
        private String lock;

        public WaitThread(String lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            synchronized (lock) {
                try {
                    System.out.println(Thread.currentThread().getName() + "  进去代码块");
                    System.out.println(Thread.currentThread().getName() + "  开始wait");
                    lock.wait();
                    System.out.println(Thread.currentThread().getName() + "   结束wait");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    static class NotifyThread extends Thread {
        private String lock;

        public NotifyThread(String lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + "  进去代码块");
                System.out.println(Thread.currentThread().getName() + "  开始notify");
                lock.notify();
                System.out.println(Thread.currentThread().getName() + "   结束开始notify");
            }
        }
    }
}

输出结果

Thread-1  进去代码块
Thread-1  开始notify
Thread-1   结束开始notify
Thread-0  进去代码块
Thread-0  开始wait

示例中开启了两个线程,一个是WaitThread,另一个是NotifyThread。NotifyThread会先启动,先调用notify方法。然后WaitThread线程才启动,调用wait方法,但是由于通知过了,wait方法就无法再获取到相应的通知,因此WaitThread会一直在wait方法出阻塞,这种现象就是通知过早的现象。针对这种现象,解决方法,一般是添加一个状态标志,让waitThread调用wait方法前先判断状态是否已经改变了没,如果通知早已发出的话,WaitThread就不再去wait。对上面的代码进行更正:

public class EarlyNotifyDemo2 {

    private static String lockObject = "";
    private static boolean isWait = true;

    public static void main(String[] args) {
        WaitThread waitThread = new WaitThread(lockObject);
        NotifyThread notifyThread = new NotifyThread(lockObject);
        notifyThread.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        waitThread.start();
    }

    static class WaitThread extends Thread {
        private String lock;

        public WaitThread(String lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            synchronized (lock) {
                try {
                    while (isWait) {
                        System.out.println(Thread.currentThread().getName() + "  进去代码块");
                        System.out.println(Thread.currentThread().getName() + "  开始wait");
                        lock.wait();
                        System.out.println(Thread.currentThread().getName() + "   结束wait");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    static class NotifyThread extends Thread {
        private String lock;

        public NotifyThread(String lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + "  进去代码块");
                System.out.println(Thread.currentThread().getName() + "  开始notify");
                lock.notifyAll();
                isWait = false;
                System.out.println(Thread.currentThread().getName() + "   结束开始notify");
            }
        }
    }
}

这段代码只是增加了一个isWait状态变量,NotifyThread调用notify方法后会对状态变量进行更新,在WaitThread中调用wait方法之前会先对状态变量进行判断,在该示例中,调用notify后将状态变量isWait改变为false,因此,在WaitThread中while对isWait判断后就不会执行wait方法,从而避免了Notify过早通知造成遗漏的情况。

总结:在使用线程的等待/通知机制时,一般都要配合一个 boolean 变量值(或者其他能够判断真假的条件),在 notify 之前改变该 boolean 变量的值,让 wait 返回后能够退出 while 循环(一般都要在 wait 方法外围加一层 while 循环,以防止早期通知),或在通知被遗漏后,不会被阻塞在 wait 方法处。这样便保证了程序的正确性

等待wait的条件发生变化

如果线程在等待时接受到了通知,但是之后等待的条件发生了变化,并没有再次对等待条件进行判断,也会导致程序出现错误。

下面用一个例子来说明这种情况

public class ConditionChangeDemo1 {

    private static List<String> lockObject = new ArrayList();

    public static void main(String[] args) {
        Consumer consumer1 = new Consumer(lockObject);
        Consumer consumer2 = new Consumer(lockObject);
        Productor productor = new Productor(lockObject);
        consumer1.start();
        consumer2.start();
        productor.start();
    }

    static class Consumer extends Thread {
        private List<String> lock;

        public Consumer(List lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            synchronized (lock) {
                try {
                    //这里使用if的话,就会存在wait条件变化造成程序错误的问题
                    if (lock.isEmpty()) {
                        System.out.println(Thread.currentThread().getName() + " list为空");
                        System.out.println(Thread.currentThread().getName() + " 调用wait方法");
                        lock.wait();
                        System.out.println(Thread.currentThread().getName() + "  wait方法结束");
                    }
                    String element = lock.remove(0);
                    System.out.println(Thread.currentThread().getName() + " 取出第一个元素为:" + element);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    static class Productor extends Thread {
        private List<String> lock;

        public Productor(List lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + " 开始添加元素");
                lock.add(Thread.currentThread().getName());
                lock.notifyAll();
            }
        }

    }

}

输出结果

Thread-0 list为空
Thread-0 调用wait方法
Thread-2 开始添加元素
Thread-1 取出第一个元素为:Thread-2
Thread-0  wait方法结束
Exception in thread "Thread-0" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0

异常原因分析:在这个例子中一共开启了3个线程,Consumer1,Consumer2以及Productor。首先Consumer1调用了wait方法后,线程处于了WAITTING状态,并且将对象锁释放出来。因此,Consumer2能够获取对象锁,从而进入到同步代块中,当执行到wait方法时,同样的也会释放对象锁。因此,productor能够获取到对象锁,进入到同步代码块中,向list中插入数据后,通过notifyAll方法通知处于WAITING状态的Consumer1和Consumer2线程。consumer1得到对象锁后,从wait方法出退出,删除了一个元素让List为空,方法执行结束,退出同步块,释放掉对象锁。这个时候Consumer2获取到对象锁后,从wait方法退出,继续往下执行,这个时候Consumer2再执行lock.remove(0);就会出错,因为List由于Consumer1删除一个元素之后已经为空了。

解决方案:通过上面的分析,可以看出Consumer2报异常是因为线程从wait方法退出之后没有再次对wait条件进行判断,因此,此时的wait条件已经发生了变化。解决办法就是,在wait退出之后再对条件进行判断即可。

public class ConditionChangeDemo2 {

    private static List<String> lockObject = new ArrayList();

    public static void main(String[] args) {
        Consumer consumer1 = new Consumer(lockObject);
        Consumer consumer2 = new Consumer(lockObject);
        Productor productor = new Productor(lockObject);
        consumer1.start();
        consumer2.start();
        productor.start();
    }

    static class Consumer extends Thread {

        private List<String> lock;

        public Consumer(List lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            synchronized (lock) {
                try {
                    //这里使用if的话,就会存在wait条件变化造成程序错误的问题
                    while (lock.isEmpty()) {
                        System.out.println(Thread.currentThread().getName() + " list为空");
                        System.out.println(Thread.currentThread().getName() + " 调用wait方法");
                        lock.wait();
                        System.out.println(Thread.currentThread().getName() + "  wait方法结束");
                    }
                    String element = lock.remove(0);
                    System.out.println(Thread.currentThread().getName() + " 取出第一个元素为:" + element);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    static class Productor extends Thread {
        private List<String> lock;

        public Productor(List lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + " 开始添加元素");
                lock.add(Thread.currentThread().getName());
                lock.notifyAll();
            }
        }

    }

}

输出结果

Thread-0 list为空
Thread-0 调用wait方法
Thread-2 开始添加元素
Thread-1 取出第一个元素为:Thread-2
Thread-0  wait方法结束
Thread-0 list为空
Thread-0 调用wait方法

上面的代码与之前的代码仅仅只是将 wait 外围的 if 语句改为 while 循环即可,这样当 list 为空时,线程便会继续等待,而不会继续去执行删除 list 中元素的代码。

总结:在使用线程的等待/通知机制时,一般都要在 while 循环中调用 wait()方法,因此配合使用一个 boolean 变量(或其他能判断真假的条件,如本文中的 list.isEmpty()),满足 while 循环的条件时,进入 while 循环,执行 wait()方法,不满足 while 循环的条件时,跳出循环,执行后面的代码。

假死状态

现象:如果是多消费者和多生产者情况,如果使用notify方法可能会出现“假死”的情况,即唤醒的是同类线程。

原因分析:假设当前多个生产者线程会调用wait方法阻塞等待,当其中的生产者线程获取到对象锁之后使用notify通知处于WAITTING状态的线程,如果唤醒的仍然是生产者线程,就会造成所有的生产者线程都处于等待状态。

解决办法:将notify方法替换成notifyAll方法,如果使用的是lock的话,就将signal方法替换成signalAll方法。

总结

在Object提供的消息通知机制应该遵循如下这些条件:

  1. 永远在while循环中对条件进行判断而不是if语句中进行wait条件的判断;
  2. 使用NotifyAll而不是使用notify。

基本的使用范式如下:

// The standard idiom for calling the wait method in Java 
synchronized (sharedObject) { 
    while (condition) { 
    sharedObject.wait(); 
        // (Releases lock, and reacquires on wakeup) 
    } 
    // do action based upon condition e.g. take or put into queue 
}

wait/notifyAll实现生产者-消费者

利用wait/notifyAll实现生产者和消费者代码如下:

public class ProductorConsumerDemo1 {

    public static void main(String[] args) {

        LinkedList linkedList = new LinkedList();
        ExecutorService service = Executors.newFixedThreadPool(15);
        for (int i = 0; i < 5; i++) {
            service.submit(new Productor(linkedList, 8));
        }

        for (int i = 0; i < 10; i++) {
            service.submit(new Consumer(linkedList));
        }

    }

    static class Productor implements Runnable {

        private List<Integer> list;
        private int maxLength;

        public Productor(List list, int maxLength) {
            this.list = list;
            this.maxLength = maxLength;
        }

        @Override
        public void run() {
            while (true) {
                synchronized (list) {
                    try {
                        while (list.size() == maxLength) {
                            System.out.println("生产者" + Thread.currentThread().getName() + "  list以达到最大容量,进行wait");
                            list.wait();
                            System.out.println("生产者" + Thread.currentThread().getName() + "  退出wait");
                        }
                        Random random = new Random();
                        int i = random.nextInt();
                        System.out.println("生产者" + Thread.currentThread().getName() + " 生产数据" + i);
                        list.add(i);
                        list.notifyAll();
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        }
    }

    static class Consumer implements Runnable {

        private List<Integer> list;

        public Consumer(List list) {
            this.list = list;
        }

        @Override
        public void run() {
            while (true) {
                synchronized (list) {
                    try {
                        while (list.isEmpty()) {
                            System.out.println("消费者" + Thread.currentThread().getName() + "  list为空,进行wait");
                            list.wait();
                            System.out.println("消费者" + Thread.currentThread().getName() + "  退出wait");
                        }
                        Integer element = list.remove(0);
                        System.out.println("消费者" + Thread.currentThread().getName() + "  消费数据:" + element);
                        list.notifyAll();
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

}

输出结果

生产者pool-1-thread-2 生产数据-703210513
生产者pool-1-thread-2 生产数据-1025434820
生产者pool-1-thread-2 生产数据70070412
生产者pool-1-thread-2 生产数据-598504371
生产者pool-1-thread-2 生产数据-716978999
生产者pool-1-thread-2 生产数据-1175198461
生产者pool-1-thread-2 生产数据-1212912406
生产者pool-1-thread-2 生产数据-332467186
生产者pool-1-thread-2  list以达到最大容量,进行wait
消费者pool-1-thread-15  消费数据:-703210513
消费者pool-1-thread-15  消费数据:-1025434820
消费者pool-1-thread-15  消费数据:70070412
消费者pool-1-thread-15  消费数据:-598504371
消费者pool-1-thread-15  消费数据:-716978999
消费者pool-1-thread-15  消费数据:-1175198461
消费者pool-1-thread-15  消费数据:-1212912406
消费者pool-1-thread-15  消费数据:-332467186
消费者pool-1-thread-15  list为空,进行wait
消费者pool-1-thread-14  list为空,进行wait
消费者pool-1-thread-13  list为空,进行wait
消费者pool-1-thread-11  list为空,进行wait
消费者pool-1-thread-12  list为空,进行wait
消费者pool-1-thread-10  list为空,进行wait
消费者pool-1-thread-9  list为空,进行wait
消费者pool-1-thread-8  list为空,进行wait
消费者pool-1-thread-7  list为空,进行wait
消费者pool-1-thread-6  list为空,进行wait
生产者pool-1-thread-5 生产数据84590545
生产者pool-1-thread-5 生产数据-1631754695

使用Lock中Condition的await/signalAll实现生产者-消费者

参照Object的wait和notify/notifyAll方法,Condition也提供了同样的方法:

针对wait方法

void await() throws InterruptedException:当前线程进入等待状态,如果其他线程调用condition的signal或者signalAll方法并且当前线程获取Lock从await方法返回,如果在等待状态中被中断会抛出被中断异常;

long awaitNanos(long nanosTimeout):当前线程进入等待状态直到被通知,中断或者超时;

boolean await(long time, TimeUnit unit)throws InterruptedException:同第二种,支持自定义时间单位

boolean awaitUntil(Date deadline) throws InterruptedException:当前线程进入等待状态直到被通知,中断或者到了某个时间

针对notify方法

void signal():唤醒一个等待在condition上的线程,将该线程从等待队列中转移到同步队列中,如果在同步队列中能够竞争到Lock则可以从等待方法中返回。

void signalAll():与signal的区别在于能够唤醒所有等待在condition上的线程

也就是说wait—>await,notify---->Signal。

如果采用lock中Conditon的消息通知原理来实现生产者-消费者问题,原理同使用wait/notifyAll一样。直接上代码:

public class ProductorConsumerDemo2 {

    private static ReentrantLock lock = new ReentrantLock();
    private static Condition full = lock.newCondition();
    private static Condition empty = lock.newCondition();

    public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
        ExecutorService service = Executors.newFixedThreadPool(15);
        for (int i = 0; i < 5; i++) {
            service.submit(new Productor(linkedList, 8, lock));
        }
        for (int i = 0; i < 10; i++) {
            service.submit(new Consumer(linkedList, lock));
        }

    }

    static class Productor implements Runnable {

        private List<Integer> list;
        private int maxLength;
        private Lock lock;

        public Productor(List list, int maxLength, Lock lock) {
            this.list = list;
            this.maxLength = maxLength;
            this.lock = lock;
        }

        @Override
        public void run() {
            while (true) {
                lock.lock();
                try {
                    while (list.size() == maxLength) {
                        System.out.println("生产者" + Thread.currentThread().getName() + "  list以达到最大容量,进行wait");
                        full.await();
                        System.out.println("生产者" + Thread.currentThread().getName() + "  退出wait");
                    }
                    Random random = new Random();
                    int i = random.nextInt();
                    System.out.println("生产者" + Thread.currentThread().getName() + " 生产数据" + i);
                    list.add(i);
                    empty.signalAll();
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    static class Consumer implements Runnable {

        private List<Integer> list;
        private Lock lock;

        public Consumer(List list, Lock lock) {
            this.list = list;
            this.lock = lock;
        }

        @Override
        public void run() {
            while (true) {
                lock.lock();
                try {
                    while (list.isEmpty()) {
                        System.out.println("消费者" + Thread.currentThread().getName() + "  list为空,进行wait");
                        empty.await();
                        System.out.println("消费者" + Thread.currentThread().getName() + "  退出wait");
                    }
                    Integer element = list.remove(0);
                    System.out.println("消费者" + Thread.currentThread().getName() + "  消费数据:" + element);
                    full.signalAll();
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }

}

输出结果

生产者pool-1-thread-1 生产数据-1633842993
生产者pool-1-thread-1 生产数据1337251950
生产者pool-1-thread-1 生产数据1310879631
生产者pool-1-thread-1 生产数据-214297115
生产者pool-1-thread-1 生产数据738937512
生产者pool-1-thread-1 生产数据13060041
生产者pool-1-thread-1 生产数据-957049554
生产者pool-1-thread-1 生产数据-1062017880
生产者pool-1-thread-1  list以达到最大容量,进行wait
生产者pool-1-thread-2  list以达到最大容量,进行wait
生产者pool-1-thread-3  list以达到最大容量,进行wait
生产者pool-1-thread-4  list以达到最大容量,进行wait
生产者pool-1-thread-5  list以达到最大容量,进行wait
消费者pool-1-thread-6  消费数据:-1633842993
消费者pool-1-thread-6  消费数据:1337251950
消费者pool-1-thread-6  消费数据:1310879631
消费者pool-1-thread-6  消费数据:-214297115
消费者pool-1-thread-6  消费数据:738937512
消费者pool-1-thread-6  消费数据:13060041
消费者pool-1-thread-6  消费数据:-957049554
消费者pool-1-thread-6  消费数据:-1062017880
消费者pool-1-thread-6  list为空,进行wait
消费者pool-1-thread-7  list为空,进行wait
消费者pool-1-thread-8  list为空,进行wait
消费者pool-1-thread-9  list为空,进行wait
消费者pool-1-thread-10  list为空,进行wait
消费者pool-1-thread-11  list为空,进行wait
消费者pool-1-thread-12  list为空,进行wait
消费者pool-1-thread-13  list为空,进行wait
消费者pool-1-thread-14  list为空,进行wait
消费者pool-1-thread-15  list为空,进行wait
生产者pool-1-thread-1  退出wait
生产者pool-1-thread-1 生产数据1949864858
生产者pool-1-thread-1 生产数据-1693880970

使用BlockingQueue实现生产者-消费者

由于BlockingQueue内部实现就附加了两个阻塞操作。即当队列已满时,阻塞向队列中插入数据的线程,直至队列中未满;当队列为空时,阻塞从队列中获取数据的线程,直至队列非空时为止。可以利用BlockingQueue实现生产者-消费者为题,阻塞队列完全可以充当共享数据区域,就可以很好的完成生产者和消费者线程之间的协作。

public class ProductorConsumerDmoe3 {

    private static LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>();

    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(15);
        for (int i = 0; i < 5; i++) {
            service.submit(new Productor(queue));
        }
        for (int i = 0; i < 10; i++) {
            service.submit(new Consumer(queue));
        }
    }

    static class Productor implements Runnable {
        private BlockingQueue queue;

        public Productor(BlockingQueue queue) {
            this.queue = queue;
        }

        @Override
        public void run() {
            try {
                while (true) {
                    Random random = new Random();
                    int i = random.nextInt();
                    System.out.println("生产者" + Thread.currentThread().getName() + "生产数据" + i);
                    queue.put(i);
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static class Consumer implements Runnable {
        private BlockingQueue queue;

        public Consumer(BlockingQueue queue) {
            this.queue = queue;
        }

        @Override
        public void run() {
            try {
                while (true) {
                    Integer element = (Integer) queue.take();
                    System.out.println("消费者" + Thread.currentThread().getName() + "正在消费数据" + element);
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

输出结果

生产者pool-1-thread-2生产数据-1056722868
生产者pool-1-thread-1生产数据-1217947426
生产者pool-1-thread-3生产数据590686437
生产者pool-1-thread-4生产数据1782376429
生产者pool-1-thread-5生产数据1558897279
消费者pool-1-thread-6正在消费数据-1056722868
消费者pool-1-thread-7正在消费数据-1217947426
消费者pool-1-thread-8正在消费数据590686437
消费者pool-1-thread-9正在消费数据1782376429
消费者pool-1-thread-10正在消费数据1558897279
生产者pool-1-thread-4生产数据1977644261
生产者pool-1-thread-3生产数据182370155
消费者pool-1-thread-11正在消费数据1977644261
生产者pool-1-thread-2生产数据949821636
生产者pool-1-thread-5生产数据1931032717
消费者pool-1-thread-13正在消费数据949821636
生产者pool-1-thread-1生产数据873417555
消费者pool-1-thread-14正在消费数据1931032717
消费者pool-1-thread-12正在消费数据182370155
消费者pool-1-thread-15正在消费数据873417555

可以看出,使用BlockingQueue来实现生产者-消费者很简洁,这正是利用了BlockingQueue插入和获取数据附加阻塞操作的特性。

关于生产者-消费者实现的三种方式,到这里就全部总结出来,如果觉得不错的话,请点赞转发,也算是给我的鼓励,在此表示感谢!

Java并发编程模式:生产者消费者模式的6种实现方式 本文旨在帮助Java开发者系统掌握生产者消费者模式的多种实现方式,理解每种方案的底层原理、适用场景及优缺点。内容覆盖从基础的到高性能的Disruptor,再到现代异步编程的,适合从初级到高级的全阶段开发者学习。用生活化案例解释核心概念分6种方式详细讲解实现原理与代码总结每种方案的适用场景与优缺点生产者:生成数据的线程(厨师)消费者:处理数据的线程(服务员)缓冲区:协调双方速度的中间容器(取餐窗口)线程安全:保证多线程操作数据一致的机制从基础的到高性能的Disruptor,再到异步的。 阅读详情

相关推荐

【无标题】ReaxFF拟合参数以及算法安装

ReaxFF拟合参数以及算法安装

zmzyzm的博客 1421

生产者消费者问题

生产者消费者问题

惜朝歌的博客 1万+

Apifox 自动化测试新增流程控制条件,复杂测试场景不再是问题

在 if 条件后的输入框填写请求接口得到的变量 viewed ,选择条件为「小于」,输入比较数值 10 ,并在 if 条件下添加「宠物下架处理」步骤。借助新增的自动化测试流程控制条件,测试人员就可以使用 Apifox 去实现更深层更多样的测试应用场景,帮助提升测试效率,减少复杂场景的测试操作。「查询新建宠物」、「修改宠物状态为已售出」和「查询宠物状态是否修改」步骤为当前在售宠物信息盘点的三个标准步骤,所以将这三个步骤合成一组。在循环中添加「查询在售宠物」和「修改宠物状态」步骤;

878

超详解“生产者消费者问题”【操作系统】

超详解“生产者消费者问题”【操作系统】

m0_59731459的博客 1万+

Matlab双目相机标定

1 概述 现在有许多双目相机在出厂时就已经标定好了,用户拿到手后可以直接使用,例如Intel Realsense系列。但是有些相机出厂的时候并没有完成标定工作,因而这个时候就需要我们自己来标定。由于笔者曾改装过一个双目相机,最远可以测至50m,因此有一定的心得体会想给大家分享一下。本文主要介绍双目相机标定的整个过程,以及导出标定数据的方法。 相机标定主要分为手动标定和自动标定,手动标定比较繁琐,这里主要介绍基于matlab工具箱的自动标定方式来对双目相机进行标定。具体的相关标定细节也可以参照这篇博客:m

weixin_46133643的博客 2万+

CS224W图神经网络实战:如何用Motifs和Graphlets分析社交网络结构?

本文基于斯坦福CS224W课程,深入探讨了如何利用网络模体(Motifs)和图元(Graphlets)分析社交网络的微观结构。通过实战代码演示,文章介绍了如何识别具有统计显著性的关键连接模式,计算节点的图元度向量以刻画局部拓扑特征,并将这些结构洞见应用于社区发现、影响力分析及图神经网络的节点特征工程,从而提升模型在虚假账号检测等任务中的性能。

2w3e4r5t6y的博客 912

采用信号量机制实现消费者与生产者的线程同步_经典生产者-消费者问题解析...

1.生产者-消费者问题生产者和消费者问题在现实系统中是很普遍的。例如在一个多媒体系统中,生产者编码视频帧,而消费者消费(解码)视频帧,缓冲区的目的就是减少视频流的抖动。又如在图形用户接口设计中,生产者检测到鼠标和键盘事件,并将其插入到缓冲区中。消费者以某种基于优先级的方式从缓冲区中取出这些事件并显示在屏幕上。生产者和消费者模式共享一个有n个槽位的有限缓冲区。生产者反复地生成新的item,并将它插入...

weixin_39603995的博客 1384

UFAST车道线检测 Ultra-Fast-Lane-Detection 从环境搭建到复现完成(一)

UFAST车道线检测 Ultra-Fast-Lane-Detection 从环境搭建到复现完成(一)

m0_46364958的博客 4345

操作系统经典问题--生产者消费者问题

生产者消费者问题在并发编程和系统设计中有广泛的应用场景,包括日志处理、任务队列、缓存更新、消息队列和实时数据处理等。这些场景都涉及到数据的生成和处理,以及需要平衡不同组件之间的速度和效率。通过应用生产者消费者模式,可以实现数据的解耦、异步处理和并发控制等目标,从而提高系统的可扩展性、可靠性和性能。

2301_79659699的博客 7880

(BS ISO 11898-1:2015)CAN_FD 总线协议详解1- 基本概念描述

本文对can总线协议做了详细的讲解,can协议里面的每一个知识点都做了详细且举一反三的论述,读了本文可以不用读can协议了。

zhangziranzm的博客 2315

ROS机器人未知环境自主探索功能包explore_lite最全源码详细解析(五)

本系列文章主要针对ROS机器人常使用的未知环境自主探索功能包explore_lite展开全源码的详细解析,并进行概括总结。 本系列文章共包含六篇文章,前五篇文章主要介绍explore_lite功能包中 explore.cpp、costmap_tools.h、frontier_search.cpp、costmap_client.cpp等源码实现文件中全部函数的详细介绍,第六篇文章进行概括总结,包含自主探索功能包explore_lite的简介,实现未知环境自主探索功能的原理及流程总结,效果演示,使用功能包exp

慕羽★的博客 1513

【操作系统中的生产者消费者问题】

在操作系统中,生产者消费者问题是一个经典的多线程同步问题,旨在解决多个进程或线程在访问共享资源时可能产生的冲突。生产者消费者问题是一个涉及多线程同步的经典问题,在这个问题中,存在两类线程,即生产者和消费者,它们共享一个固定大小的缓冲区。生产者的任务是生成数据、填充缓冲区,而消费者则从缓冲区取出数据。临界区是指一段访问共享资源(如共享内存、文件等)的代码。在任何时刻,只能有一个线程执行临界区内的代码。如果多个线程同时进入临界区,就可能导致数据损坏或不一致。

武帝为此的博客 6462

ISO13849功能安全从入门到精通(3):安全功能及其对降低风险的贡献4

例如,如果机器必须在有限速度下运行时进行设置,那么在低加速度值下进行设置时,参数P1将是正确的选择:随着危险缓慢出现并给予足够的运动自由,操作员将能够移出危险区域。例如,在无故障的条件下,在操作者的手的方向移动的滚子不能陷住它。在所有其他情况下,F1是正确的选择,前提是危险暴露的持续时间不超过机器总操作时间的1/20。需求来源于必要的风险降低。由于这些考虑而降低的PLr在任何情况下都不得低于通过比较方式考虑的机器的PLr,因为并不能从事故发生率低和严重程度低得出实施的安全功能提供的安全水平高于要求。

wdconan001的博客 837

实现生产者消费者有几种方式?能一一列出来吗?

推荐阅读: 我总结了72份面试题,累计3170页,斩获了30+互联网公司offer(含BATJM) 2020首战告捷,这份Java面试神技Plus版,让我成功拿到了阿里、京东、字节跳动等大厂offer 膜拜!阿里内部都在强推的K8S(kubernetes)学习指南,不能再详细了 生产者-消费者模式是一个十分经典的多线程并发协作的模式,弄懂生产者-消费者问题能够让我们对并发编程的理解加深。所谓生产者-消费者问题,实际上主要是包含了两类线程,一种是生产者线程用于生产数据,另一种是消费者线程用于消

资料免费分享,点击名片 271

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

Java生产者、消费者模式的几种实现方式

文章目录方式一:BlockingQueue方式(最优方式)方式二:Synchronized+wait/notifyAll方式方式三:ReentrantLock+Condition方式几种方式对比参考 方式一:BlockingQueue方式(最优方式) private static final String THREAD_PRODUCE = "生产者"; private static final String THREAD_CONSUME = "消费者"; //阻塞队列中最多存5个 private stati

小马快跑的博客 1394

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

生产者消费者模式的几种实现方式以及线程安全问题

生产者消费者模式的几种实现方式拿我们生活中的例子来说,工厂生产出来的产品总是要输出到外面使用的,这就是生产与消费的概念。 在我们实际的软件开发过程中,经常会碰到如下场景:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类、函数、线程、进程等)。 产生数据的模块,就形象地称为生产者;而处理数据的模块,就称为消费者。 第一种:采用wait—notify实现生产者消费者

noaman_wgs的博客 1万+

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

生产者消费者模式的几种实现方式(阻塞队列)

非原创,转自https://blog.youkuaiyun.com/noaman_wgs/article/details/66969905 生产者消费者模式的几种实现方式 拿我们生活中的例子来说,工厂生产出来的产品总是要输出到外面使用的,这就是生产与消费的概念。 在我们实际的软件开发过程中,经常会碰到如下场景:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类、函数、线程...

weixin_42581112的博客 1969

实现生产者与消费者问题的几种方式

生产者与消费者问题是多线程同步的一个经典问题。生产者和消费者同时使用一块缓冲区,生产者生产商品放入缓冲区,消费者从缓冲区中取出商品。我们需要保证的是,当缓冲区满时,生产者不可生产商品;当缓冲区为空时,消费者不可取出商品。 下面介绍java中几种解决同步问题的方式 wait()与notify()方法 Lock与Condition机制 BlockingQueue阻塞队列 ...

Talk is cheap,show me the code. 1007

设计模式——生产者消费者模式

1 基本概括 2 主要介绍 2.1 概念 生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力 这个阻塞队列就是用来给生产者和消费者解耦的。 如果缓冲区已经满了,则生产者线程阻塞; 如果缓冲区为空,那么消费者线程阻塞。 2.2 生产消费者模型 就是在一

qq_20761351的博客 1万+

几种生产者消费者的实现

sychronized /* * 生产者和消费者案例 * 有可能出现的问题;添加或创建数据的线程叫生产者线程,删除和销毁数据的线程为消费者线程 * 由于多线程环境中,有可能存在生产者线程过快 导致数据丢失,或者消费者过快 导致消费不到数据,这时候就需要加入【等待唤醒机制】 * 生产者线程过快 达到条件就需要wait(),消费者线程 没有数据消费也需要wait(); */ public c...

wuzhenwei0419的博客 160

java 生产者与消费者的几种实现方式

java 生产者与消费者的几种实现方式 1 synchronized的方式 public class MyClass { public static void main(String[] args){ final DataAssemble dataAssemble = new DataAssemble(); for(int i = 0; i < 20; i ++){ Thread thread = new Thread(new Run

chao18867105705的博客 242

生产者-消费者模式的三种实现方式

生产者-消费者模式的三种实现方式 1、背景 生产者生产数据到缓冲区中,消费者从缓冲区中取数据。 如果缓冲区已经满了,则生产者线程阻塞; 如果缓冲区为空,那么消费者线程阻塞。 2、方式一:synchronized、wait和notify package producerConsumer; //wait 和 notify public class ProducerConsumerWithWaitNofity { public static void main(String[] args) {

weixin_42821400的博客 989

生产者消费者问题的几种实现

synchronized实现生产者消费者模型 ReentrantLock实现生产者消费者模型 BlockingQueue实现生产者消费者模型 Semaphore实现生产者消费者模型       生产者消费者问题是一个经典的问题,一般情况下都会使用synchronized关键字来对生产和消费逻辑进行加锁 ,最近学习了下并发编程相关的基础知识,尝试使用其它的几种方法来实现生产者和消费者模型。 ...

laomumu1992的博客 671

【Java总结】生产者消费者模型

生产者消费者模型主要结构如下,是一个典型的线程同步的案例。下面就来使用java做几种线程同步的方式来实现以下该模型 确保一个生产者消费者模型的稳定运行的前提有以下几个 生成者应该具备持续生成的能力 消费者应该具备持续消费的能力 生产者的生成和消费消费有一定的阀值,如生成总量到100需要停止生产,通知消费;消费到0的时候停止消费开始生产; wait,notify方案阻塞队列的实现Reentr...

zhujohnle的专栏 8979

并发编程中生产者与消费者几种实现方式

wait、notify public class ProducerAndConsumer { public static void main(String[] args) { GoodsFactory goodsFactory = new GoodsFactory(); new Producer(goodsFactory).start(); new Consumer(goodsFactory).start(); } } class Produ

自律使我自由 457

多种方式解决生产者消费者问题

一、问题描述 生产者消费者问题(Producer-consumer problem),也称有限缓冲问题(Bounded-buffer problem),是一个多线程同步问题的经典案例。生产者生成一定量的数据放到缓冲区中,然后重复此过程;与此同时,消费者也在缓冲区消耗这些数据。生产者和消费者之间必须保持同步,要保证生产者不会在缓冲区满时放入数据,消费者也不会在缓冲区空时消耗数据。不够完善的解决方法容易出现死锁的情况,此时进程都在等待唤醒。 示意图: 二、解决方法 思路 采用某种机制保护生.

可小辉的博客 2348
上一篇: Spring Cloud Alibaba 之 Sentinel详解
下一篇: 这份让腾讯、阿里巴巴都羡慕不已的MySQL全面手册,受喜爱程度不输任何大厂笔记!
「已注销」
博客等级 码龄6年 36粉丝 149原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值