简体   繁体   English

Java线程程序无法使用wait()和notifyAll()

[英]Java thread program not working using wait() and notifyAll()

Below is my program. 以下是我的计划。 Always the thread 0 gets the printer, other threads do not get it. 始终线程0获取打印机,其他线程不获取它。 There is one printer object, and i want multiple job threads to use the printer. 有一个打印机对象,我想要多个作业线程来使用打印机。 How to make this program work so that all jobs get the printer. 如何使该程序工作,以便所有作业都能获得打印机。 For me the code flow seems to be fine. 对我来说,代码流似乎很好。 Am synchronizing on a single printer object. 我正在同一个打印机对象上同步。 Please help. 请帮忙。

    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();
            }
        }

    }

Hi, I have revised my program for getting lock first then condition checking. 嗨,我已经修改了我的程序,先获取锁定然后进行条件检查。 Even then the thread 0 always gets the printer. 即使这样,线程0总是得到打印机。 Other threads starve. 其他线程饿死。

Revised program: 修订计划:

    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();
                    }
                }
            }

        }

    }

I think what you are looking for is a Condition . 我认为你所寻找的是Condition You first need to obtain a lock, then you can check a condition. 您首先需要获得一个锁,然后您可以检查一个条件。 While that condition hold the thread will sleep. 虽然那个条件持有线程会睡觉。 When the condition no longer holds the sleeping thread (or next sleeping thread) is woken up to check the condition again. 当条件不再保持时,睡眠线程(或下一个睡眠线程)被唤醒以再次检查该条件。

You can read more about the Condition object here: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html 您可以在此处阅读有关Condition对象的更多信息: http//docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html

If you want the resource to be available for all the threads in fair manner, it's much better to use ReentrantLock with fair = true parameter. 如果您希望资源以公平的方式可用于所有线程,那么使用带有fair = true参数的ReentrantLock会更好。 Also never rely on non-volatile variables changed in concurrent way. 也永远不要依赖于并发方式改变的非易失性变量。 Here's the fixed code: 这是固定代码:

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();
        }
    }
}

It should look like this: 它应该如下所示:

  1. Acquire the printer: 购买打印机:

     synchronized (PrinterQueue.printer) { while (PrinterQueue.printer.isUsed()) { try { System.out.println(Thread.currentThread() + "WAITING FOR PRINTER"); PrinterQueue.printer.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread() + "GOT PRINTER"); PrinterQueue.printer.setUsed(true); } 
  2. Use the printer, dummied as per your code by Thread.sleep() : 使用打印机,按照您的代码通过Thread.sleep()进行Thread.sleep()

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

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

You need to use while rather than if , and you need to test the same object you're synchronized on. 您需要使用while而不是if ,并且需要测试同步的同一对象。 And use notifyAll() rather than notify(). 并使用notifyAll()而不是notify().

But it isn't clear to me that you need any of this, just a synchronized block. 但我不清楚你需要这些,只是一个synchronized块。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM