有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!


共 (6) 个答案

  1. # 1 楼答案

    即使你特别要求wait()notify(),我觉得这句话仍然很重要:

    Josh Bloch,有效Java第二版,第69项:与waitnotify相比,更喜欢并发实用程序(强调his):

    Given the difficulty of using wait and notify correctly, you should use the higher-level concurrency utilities instead [...] using wait and notify directly is like programming in "concurrency assembly language", as compared to the higher-level language provided by java.util.concurrent. There is seldom, if ever, reason to use wait and notify in new code.

  2. # 2 楼答案

    不是队列示例,但非常简单:)

    class MyHouse {
        private boolean pizzaArrived = false;
    
        public void eatPizza(){
            synchronized(this){
                while(!pizzaArrived){
                    wait();
                }
            }
            System.out.println("yumyum..");
        }
    
        public void pizzaGuy(){
            synchronized(this){
                 this.pizzaArrived = true;
                 notifyAll();
            }
        }
    }
    

    一些要点:
    1) 决不

     if(!pizzaArrived){
         wait();
     }
    

    始终使用while(条件),因为

    • a) 线程可以偶尔唤醒 从等待状态而不是 任何人通知的。(即使 披萨人没按门铃, 有人会决定试着吃这个 比萨饼)
    • b) 你应该检查一下 在获得 同步锁。比如说比萨饼 不要永远持续下去。你醒了, 排队买比萨饼,但不是 对每个人都足够了。如果你不 检查,你可能会吃纸 (更好的例子可能是 while(!pizzaExists){ wait(); }

    2)在调用wait/nofity之前,必须先保持锁(已同步)。线程还必须在唤醒之前获得锁

    3)尽量避免在同步块中获取任何锁,尽量不要调用外来方法(您不知道它们在做什么的方法)。如果必须这样做,一定要采取措施避免僵局

    4)小心使用notify()。坚持使用notifyAll()直到你知道你在做什么

    5)最后,但并非最不重要的一点,阅读Java Concurrency in Practice

  3. # 3 楼答案

    wait()notify()方法旨在提供一种机制,允许线程阻塞,直到满足特定条件为止。为此,我假设您想要编写一个阻塞队列实现,其中有一些固定大小的元素备份存储

    您必须做的第一件事是确定希望方法等待的条件。在这种情况下,您希望put()方法阻塞,直到存储中有可用空间为止,并且希望take()方法阻塞,直到有一些元素返回为止

    public class BlockingQueue<T> {
    
        private Queue<T> queue = new LinkedList<T>();
        private int capacity;
    
        public BlockingQueue(int capacity) {
            this.capacity = capacity;
        }
    
        public synchronized void put(T element) throws InterruptedException {
            while(queue.size() == capacity) {
                wait();
            }
    
            queue.add(element);
            notify(); // notifyAll() for multiple producer/consumer threads
        }
    
        public synchronized T take() throws InterruptedException {
            while(queue.isEmpty()) {
                wait();
            }
    
            T item = queue.remove();
            notify(); // notifyAll() for multiple producer/consumer threads
            return item;
        }
    }
    

    关于必须使用wait和notify机制的方式,有几点需要注意

    首先,您需要确保对wait()notify()的任何调用都在同步的代码区域内(在同一对象上同步wait()notify()调用)。出现这种情况的原因(标准螺纹安全问题除外)是由于一种称为丢失信号的情况

    例如,当队列恰好已满时,线程可能会调用put(),然后检查条件,确定队列已满,但在它阻止另一个线程调度之前。然后,第二个线程take()从队列中获取一个元素,并通知等待的线程队列不再满。但是,因为第一个线程已经检查了条件,所以在重新调度后,它将只调用wait(),即使它可能会取得进展

    通过在共享对象上进行同步,可以确保不会发生此问题,因为在第一个线程实际阻塞之前,第二个线程的take()调用将无法进行

    其次,由于一个被称为虚假唤醒的问题,您需要将正在检查的条件放入while循环,而不是if语句。在这里,等待的线程有时可以在不调用notify()的情况下重新激活。将此检查放入while循环将确保如果出现虚假唤醒,将重新检查条件,并且线程将再次调用wait()


    正如其他一些答案所提到的,Java1.5引入了一个新的并发库(在java.util.concurrent包中),该库旨在提供对等待/通知机制的更高级别抽象。使用这些新功能,您可以像这样重写原始示例:

    public class BlockingQueue<T> {
    
        private Queue<T> queue = new LinkedList<T>();
        private int capacity;
        private Lock lock = new ReentrantLock();
        private Condition notFull = lock.newCondition();
        private Condition notEmpty = lock.newCondition();
    
        public BlockingQueue(int capacity) {
            this.capacity = capacity;
        }
    
        public void put(T element) throws InterruptedException {
            lock.lock();
            try {
                while(queue.size() == capacity) {
                    notFull.await();
                }
    
                queue.add(element);
                notEmpty.signal();
            } finally {
                lock.unlock();
            }
        }
    
        public T take() throws InterruptedException {
            lock.lock();
            try {
                while(queue.isEmpty()) {
                    notEmpty.await();
                }
    
                T item = queue.remove();
                notFull.signal();
                return item;
            } finally {
                lock.unlock();
            }
        }
    }
    

    当然,如果您确实需要阻塞队列,那么应该使用BlockingQueue接口的实现

    另外,对于像这样的东西,我强烈推荐Java Concurrency in Practice,因为它涵盖了您想要了解的关于并发相关问题和解决方案的所有内容

  4. # 4 楼答案

    该问题要求等待()+通知(),涉及队列(缓冲区)。首先想到的是使用缓冲区的生产者-消费者场景

    我们系统的三个组成部分:

    1. 队列[缓冲区]-线程之间共享的固定大小队列
    2. Producer—线程生成/向缓冲区插入值
    3. 使用者-线程使用/删除缓冲区中的值

    生产者线程: 生产者在缓冲区中插入值,直到缓冲区已满。 如果缓冲区已满,生产者调用wait()并进入等待阶段,直到消费者唤醒它

        static class Producer extends Thread {
        private Queue<Integer> queue;
        private int maxSize;
    
        public Producer(Queue<Integer> queue, int maxSize, String name) {
            super(name);
            this.queue = queue;
            this.maxSize = maxSize;
        }
    
        @Override
        public void run() {
            while (true) {
                synchronized (queue) {
                    if (queue.size() == maxSize) {
                        try {
                            System.out.println("Queue is full, " + "Producer thread waiting for " + "consumer to take something from queue");
                            queue.wait();
                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }
                    }
                    Random random = new Random();
                    int i = random.nextInt();
                    System.out.println(" ^^^ Producing value : " + i);
                    queue.add(i);
                    queue.notify();
                }
                sleepRandom();
            }
        }
    }
    

    消费者线程: 使用者线程从缓冲区中移除值,直到缓冲区为空。 如果缓冲区为空,则使用者调用wait()方法并进入等待状态,直到生产者发送通知信号

        static class Consumer extends Thread {
        private Queue<Integer> queue;
        private int maxSize;
    
        public Consumer(Queue<Integer> queue, int maxSize, String name) {
            super(name);
            this.queue = queue;
            this.maxSize = maxSize;
        }
    
        @Override
        public void run() {
            Random random = new Random();
            while (true) {
                synchronized (queue) {
                    if (queue.isEmpty()) {
                        System.out.println("Queue is empty," + "Consumer thread is waiting" + " for producer thread to put something in queue");
                        try {
                            queue.wait();
                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }
                    }
                    System.out.println(" vvv Consuming value : " + queue.remove());
                    queue.notify();
                }
                sleepRandom();
            }
        }
    }
    

    UTIL方法:

        public static void sleepRandom(){
        Random random = new Random();
        try {
            Thread.sleep(random.nextInt(250));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    

    申请代码:

        public static void main(String args[]) {
        System.out.println("How to use wait and notify method in Java");
        System.out.println("Solving Producer Consumper Problem");
        Queue<Integer> buffer = new LinkedList<>();
        int maxSize = 10;
        Thread producer = new Producer(buffer, maxSize, "PRODUCER");
        Thread consumer = new Consumer(buffer, maxSize, "CONSUMER");
        producer.start();
        consumer.start();
    }
    

    示例输出:

     ^^^ Producing value : 1268801606
     vvv Consuming value : 1268801606
    Queue is empty,Consumer thread is waiting for producer thread to put something in queue
     ^^^ Producing value : -191710046
     vvv Consuming value : -191710046
     ^^^ Producing value : -1096119803
     vvv Consuming value : -1096119803
     ^^^ Producing value : -1502054254
     vvv Consuming value : -1502054254
    Queue is empty,Consumer thread is waiting for producer thread to put something in queue
     ^^^ Producing value : 408960851
     vvv Consuming value : 408960851
     ^^^ Producing value : 2140469519
     vvv Consuming value : 65361724
     ^^^ Producing value : 1844915867
     ^^^ Producing value : 1551384069
     ^^^ Producing value : -2112162412
     vvv Consuming value : -887946831
     vvv Consuming value : 1427122528
     ^^^ Producing value : -181736500
     ^^^ Producing value : -1603239584
     ^^^ Producing value : 175404355
     vvv Consuming value : 1356483172
     ^^^ Producing value : -1505603127
     vvv Consuming value : 267333829
     ^^^ Producing value : 1986055041
    Queue is full, Producer thread waiting for consumer to take something from queue
     vvv Consuming value : -1289385327
     ^^^ Producing value : 58340504
     vvv Consuming value : 1244183136
     ^^^ Producing value : 1582191907
    Queue is full, Producer thread waiting for consumer to take something from queue
     vvv Consuming value : 1401174346
     ^^^ Producing value : 1617821198
     vvv Consuming value : -1827889861
     vvv Consuming value : 2098088641
    
  5. # 5 楼答案

    范例

    public class myThread extends Thread{
         @override
         public void run(){
            while(true){
               threadCondWait();// Circle waiting...
               //bla bla bla bla
            }
         }
         public synchronized void threadCondWait(){
            while(myCondition){
               wait();//Comminucate with notify()
            }
         }
    
    }
    public class myAnotherThread extends Thread{
         @override
         public void run(){
            //Bla Bla bla
            notify();//Trigger wait() Next Step
         }
    
    }
    
  6. # 6 楼答案

    你看过这个吗

    此外,我建议你远离在真实软件中玩这种东西。使用它很好,这样您就知道它是什么,但是并发性到处都有陷阱。如果您正在为其他人构建软件,最好使用更高级别的抽象和同步集合或JMS队列

    至少我是这么做的。我不是并发专家,所以尽可能避免手动处理线程