Java线程程序使用wait()和notifyAll()无法正常工作

4
以下是我的程序。一直是线程0获得打印机,其他线程没有获得它。有一个打印机对象,我希望多个作业线程使用打印机。如何使这个程序工作,让所有工作都能获得打印机。对我来说,代码流似乎是正常的。我正在同步单个打印机对象。请帮忙。
    package classesTesting;

    public class PrinterQueue {

        final static Printer printer = new Printer();;

        public static void main(String[] args) {
            // TODO Auto-generated method stub
            System.out.println("In Main");

            for (int i = 0; i < 5; i++) {
                new Thread(new Jobs(), "Thread - " + i).start();
                System.out.println("started " + i + " thread");
            }

        }

    }

    class Printer {
        private boolean isUsed;

        Printer() {
            this.isUsed = false;
        }

        public void setUsed(boolean used) {
            this.isUsed = used;
        }

        public boolean isUsed() {

            return this.isUsed;
        }
    }

    class Jobs implements Runnable {

        String name;
        boolean isDataAvailble;

        Jobs() {        
            this.isDataAvailble = true;
        }

        public void setNoData(boolean noData) {
            this.isDataAvailble = false;
        }

        @Override
        public void run() {

            while (isDataAvailble) {

                if (PrinterQueue.printer.isUsed()) {
                    try {
                        System.out.println(Thread.currentThread()
                                + "WAITING FOR PRINTER");
                        synchronized (PrinterQueue.printer) {
                            PrinterQueue.printer.wait();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                } else {
                    synchronized (PrinterQueue.printer) {
                        System.out.println(Thread.currentThread() + "GOT PRINTER");
                        PrinterQueue.printer.setUsed(true);
                        try {
                            Thread.sleep(3000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        PrinterQueue.printer.setUsed(false);
                        PrinterQueue.printer.notify();
                    }
                }
            }

            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

你好,我已经修改了我的程序,先获取锁再进行条件检查。即使如此,线程0仍然始终获得打印机。其他线程会挨饿。

修改后的程序:

    package classesTesting;

    public class PrinterQueue {

        static Printer printer;

        public static void main(String[] args) {
            // TODO Auto-generated method stub
            System.out.println("In Main");

            printer = new Printer();

            for (int i = 0; i < 5; i++) {
                Jobs j1 = new Jobs();
                j1.setPrinter(printer);

                Thread t1 = new Thread(j1, "Thread - " + i);
                t1.start();

                System.out.println("started " + i + " thread");
            }

        }

    }

    class Printer {
        private boolean isUsed;

        Printer() {
            this.isUsed = false;
        }

        public void setUsed(boolean used) {
            this.isUsed = used;
        }

        public boolean isUsed() {

            return this.isUsed;
        }
    }

    class Jobs implements Runnable {

        String name;
        Printer printer;

        public Printer getPrinter() {
            return printer;
        }

        public void setPrinter(Printer printer) {
            this.printer = printer;
        }

        boolean isDataAvailble;

        Jobs() {
            this.isDataAvailble = true;
        }

        public void setNoData(boolean noData) {
            this.isDataAvailble = false;
        }

        @Override
        public void run() {

            while (isDataAvailble) {
                synchronized (PrinterQueue.printer) {
                    if (this.printer.isUsed()) {
                        try {
                            System.out.println(Thread.currentThread()
                                    + "WAITING FOR PRINTER");

                            PrinterQueue.printer.wait();

                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                    else {

                        System.out.println(Thread.currentThread() + "GOT PRINTER");

                        PrinterQueue.printer.setUsed(true);

                        try {
                            Thread.sleep(3000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                        PrinterQueue.printer.setUsed(false);
                        PrinterQueue.printer.notify();
                    }
                }
            }

        }

    }
3个回答

1
我认为你需要的是一个“条件(Condition)”。首先,您需要获取锁,然后可以检查条件。当条件成立时,线程将休眠。当条件不再成立时,睡眠的线程(或下一个睡眠的线程)将被唤醒以再次检查条件。
你可以在这里阅读更多关于“条件(Condition)”对象的信息:http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html

1
如果您希望资源公平地对所有线程可用,最好使用 ReentrantLock 并设置参数 fair = true。同时,永远不要依赖于以并发方式更改的非易失性变量。以下是修复后的代码:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class PrinterQueue {
    static Printer printer;

    public static void main(String[] args) {
        System.out.println("In Main");
        printer = new Printer();
        for (int i = 0; i < 5; i++) {
            // I added printer constructor parameter to pass the same printer
            // to all the Jobs
            new Thread(new Jobs(printer), "Thread - " + i).start();
            System.out.println("started " + i + " thread");
        }
    }
}

class Printer {
    // internally printer holds a fair ReentrantLock
    Lock lock = new ReentrantLock(true);

    // call this to get the printer
    public void acquire() {
        lock.lock();
    }

    // call this to release the printer, so it's available for other threads
    public void release() {
        lock.unlock();
    }
}

class Jobs implements Runnable {
    // Declare isDataAvailble as volatile as you're going to change it from another thread
    volatile boolean isDataAvailble;
    private final Printer printer;

    // constructor now takes the printer argument
    Jobs(Printer printer) {
        this.isDataAvailble = true;
        this.printer = printer;
    }

    @Override
    public void run() {
        try {
            while (isDataAvailble) {
                System.out.println(Thread.currentThread()
                        + "Trying to get the printer");
                // get the printer
                this.printer.acquire();
                try {
                    System.out.println(Thread.currentThread()
                            + "Printer acquired!");
                    // use it
                    Thread.sleep(3000);
                } finally {
                    // Release the printer. Better to do it in finally block
                    // so you will release it even if some unexpected exception occurs
                    this.printer.release();
                }
            }

            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

感谢输入。 - Radhika Praveen

0

它应该看起来像这样:

  1. 获取打印机:

    synchronized (PrinterQueue.printer) {
        while (PrinterQueue.printer.isUsed()) {
            try {
                System.out.println(Thread.currentThread()
                                + "等待打印机");
                PrinterQueue.printer.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread() + "获取打印机");
        PrinterQueue.printer.setUsed(true);
    }
    
  2. 使用打印机,根据您的代码通过Thread.sleep()进行虚拟:

    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    
  3. 释放打印机:

    synchronized (PrinterQueue.printer) {
        PrinterQueue.printer.setUsed(false);
        PrinterQueue.printer.notifyAll();
    }
    

你需要使用while而不是if,并且需要测试与你同步的相同对象。而且使用notifyAll()而不是notify()

但我不确定你是否需要这些,只需要一个synchronized块即可。


嗨EJP,感谢您的输入。但是如果我使用您的代码,我会得到java.lang.IllegalMonitorStateException异常。因为您没有将notifyAll()放在同步块中。 - Radhika Praveen
@027 这正是我所说的,而且似乎完全与您之前编辑前所说的相反。 - user207421
嗨EJP,谢谢。但我已经尝试过了。这里的问题是没有一个线程进入等待状态。是的,我可以在不使用wait()和notifyAll()的情况下做到这一点,但我想知道为什么它不起作用,以及我是否理解wait()和notify()的概念有误。 - Radhika Praveen
你已经尝试了什么?在使用打印机对象时,只需对其进行同步即可。如果您确实需要使用所有这些内容,则需要像我上面展示的那样将其分开。 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接