首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >MultiThreading .wait() .notify()

MultiThreading .wait() .notify()
EN

Code Review用户
提问于 2015-12-28 17:36:46
回答 1查看 144关注 0票数 2

我一直在做一个很长的项目,试图了解多线程的基础知识。该应用程序应该模拟一个商店和一个共享一个Box和一个PayPal帐户的客户。

Main.Java

代码语言:javascript
复制
package exe5;

    public class Main {

        static Thread mainThread = Thread.currentThread();

        public static void main(String[] args) throws InterruptedException{
            Box box = new Box();
            PayPalAcc pp1 = new PayPalAcc();
            mainThread.setName("mainThread");

            Thread DaniCustomerThread = new Thread(new Customer(box, pp1, "Daniel Netzer"), Customer.getName());
            Thread POShopThread = new Thread (new Shop(box, pp1, "Post Office"), Shop.getName());

            DaniCustomerThread.start();
            POShopThread.start();

            Thread.sleep(5);

            // Closing all loops and adding signature to log of safe closure.
            DaniCustomerThread.interrupt();
            POShopThread.interrupt();

            Thread.sleep(100); // Allowing main thread a bit more to actually get the signature for safe closure for both threads.
        }

    }

Customer.java

代码语言:javascript
复制
package exe5;

public class Customer implements Runnable{

    private static Box box;
    private static PayPalAcc paypal;
    private static String name;
    private static Object customerLock = new Object();

    public Customer(Box box,PayPalAcc paypal, String name){
        Customer.box = box;
        Customer.name = name;
        Customer.setPaypal(paypal);
    }

    public static Object getCustomerLockMonitor(){
        return Customer.customerLock;
    }

    public static String getName() {
        return name;
    }

    // Unused Getters/Setters
    /*private synchronized void setName(String name) {
        Customer.name = name;
    }

    private static Box getBox() {
        return box;
    }

    private synchronized void setBox(Box box) {
        Customer.box = box;
    }*/

    private static PayPalAcc getPaypal() {
        return paypal;
    }

    private static synchronized void setPaypal(PayPalAcc paypal) {
        Customer.paypal = paypal;
    }

    private synchronized void  printsCustomer(int caseNum){
        switch(caseNum){
        case 1: System.out.println(Customer.getName() +" package have been withdrawed from his box at " +Shop.getName());
                break;
        case 2: System.out.println(Customer.getName() +" deposited money into shared PayPal account.");
                break;
        case 3: System.out.println(Customer.getName() +" box is empty, waiting for a new package to arrive.");
                break;
        case 4: System.out.println("closing safely " +Thread.currentThread().getName() +" thread.");
                break;
        }
    }

    private synchronized void  withdrawBox(){
        Customer.box.setBoxStatus(false);
        printsCustomer(1);
        synchronized(Shop.getShopBoxMonitor()){
            Shop.getShopBoxMonitor().notify();}
    }

    private synchronized void depositInPayPal(int amountToDeposit){
        Customer.getPaypal().setPayPalAccStatus(Customer.getPaypal().getPayPalAccStatus() + amountToDeposit);
        printsCustomer(2);
        synchronized (Shop.getShopPPMonitor()){
            Shop.getShopPPMonitor().notify();}
    }

    @Override
    public void run() {
        while(Main.mainThread.isAlive()){
            while(!box.isBoxStatus()){
                synchronized(Customer.getCustomerLockMonitor()){
                    try {
                        printsCustomer(3);
                        depositInPayPal(100);
                        Customer.getCustomerLockMonitor().wait();
                    } catch (InterruptedException e) {
                        printsCustomer(4);
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }
            if (Thread.currentThread().isInterrupted()) { break; }
            withdrawBox();
        }
    }
}

Shop.java

代码语言:javascript
复制
package exe5;

public class Shop implements Runnable{

    private static Box box;
    private static PayPalAcc paypal;
    private static String name;
    private static Object shopBox = new Object();
    private static Object shopPP = new Object();

    public Shop(Box box,PayPalAcc paypal, String name){
        Shop.box = box;
        Shop.name = name;
        Shop.setPaypal(paypal);
    }

    public static Object getShopBoxMonitor(){
        return Shop.shopBox;
    }

    public static Object getShopPPMonitor() {
        return Shop.shopPP;
    }

    public static void setShopPP(Object shopPP) {
        Shop.shopPP = shopPP;
    }

    public static String getName() {
        return name;
    }

    // Unused Getters/Setters
    /*private synchronized void setName(String name) {
        Shop.name = name;
    }

    private static Box getBox() {
        return box;
    }

    private synchronized void setBox(Box box) {
        Shop.box = box;
    }*/

    private static PayPalAcc getPaypal() {
        return paypal;
    }

    private static synchronized void setPaypal(PayPalAcc paypal) {
        Shop.paypal = paypal;
    }

    private synchronized void  depositBox(){
        Shop.box.setBoxStatus(true);
        printsShop(1);
        synchronized(Customer.getCustomerLockMonitor()){
            Customer.getCustomerLockMonitor().notify();}
    }

    private synchronized void withdrawFromPayPal(int amountToWithdraw){
        Shop.getPaypal().setPayPalAccStatus(Shop.getPaypal().getPayPalAccStatus() - amountToWithdraw);
        printsShop(2);
    }

    private synchronized void  printsShop(int caseNum){
        switch(caseNum){
        case 1: System.out.println(Shop.getName() +" deposited new package in " +Customer.getName() +" box.");
                break;
        case 2: System.out.println(Shop.getName() +" withdrawed money from shared PayPal account.");
                break;
        case 3: System.out.println(Shop.getName() +" box is full, waiting for customer withdrawal.");
                break;
        case 4: System.out.println(Customer.getName() +" did not deposited money into PayPal account yet.");
                break;
        case 5: System.out.println("closing safely " +Thread.currentThread().getName() +" thread.");
                break;
        }
    }

    @Override
    public void run() {
        while(Main.mainThread.isAlive()){
            while(Shop.getPaypal().getPayPalAccStatus() < 100){
                synchronized(Shop.getShopPPMonitor()){
                    try {
                        printsShop(4);
                        Shop.getShopPPMonitor().wait();
                    } catch (InterruptedException e) {
                        printsShop(5);
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
                withdrawFromPayPal(100);
                while(box.isBoxStatus()){
                    synchronized(Shop.getShopBoxMonitor()){
                        try {
                            printsShop(3);
                            Shop.getShopBoxMonitor().wait();
                        } catch (InterruptedException e) {
                            printsShop(5);
                            Thread.currentThread().interrupt();
                            break;
                        }
                    }
                }
                depositBox();
            }
            if (Thread.currentThread().isInterrupted()) { break; }
        }
    }
}

Box.java

代码语言:javascript
复制
package exe5;

public class Box {

    private static boolean boxStatus;

    public Box(){
        Box.boxStatus = false;
    }

    public boolean isBoxStatus() {
        return boxStatus;
    }

    public void setBoxStatus(boolean boxStatus) {
        Box.boxStatus = boxStatus;
    }

}

PayPalAcc.java

代码语言:javascript
复制
package exe5;

public class PayPalAcc {

    private static int payPalAccStatus;

    public PayPalAcc(){
        this.setPayPalAccStatus(0);
    }

    public int getPayPalAccStatus() {
        return payPalAccStatus;
    }

    public void setPayPalAccStatus(int payPalAccStatus) {
        PayPalAcc.payPalAccStatus = payPalAccStatus;
    }

}
EN

回答 1

Code Review用户

回答已采纳

发布于 2015-12-29 19:32:10

代码语言:javascript
复制
        Thread DaniCustomerThread = new Thread(new Customer(box, pp1, "Daniel Netzer"), Customer.getName());
        Thread POShopThread = new Thread (new Shop(box, pp1, "Post Office"), Shop.getName());

直接使用线程通常是个坏主意。最好将线程管理推迟到ExecutorService,并让该服务为您控制线程管理。启动代码通常看起来像

代码语言:javascript
复制
// There are several kinds of Executor available, choose what is suitable.
ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(new Customer(box, pp1, "Daniel Netzer"));
executor.submit(new Shop(box, pp1, "Post Office"));

这将启动在后台运行的任务。当您想要取消任务的执行时,请关闭执行器。

代码语言:javascript
复制
 // Not executor.shutdown(), which has a different purpose
executor.shutdownNow();

shutdownNow将中断所有正在运行的线程。

在这样做时,我们暂时失去了控制线程名的能力。有一些方法可以配置执行器使用的ThreadFactory,但是它们不太适合您的用例。相反,你应该借用这个好主意:

代码语言:javascript
复制
mainThread.setName("mainThread");

但是,与其将其应用于主线程(老实说,我不确定是否有大量的附加值),不如将其添加到run()方法的逻辑中

代码语言:javascript
复制
public class Customer implements Runnable{
    @Override
    public void run() {
        Thread.currentThread().setName(this.getName());
        // ...
    }
}

你在运行方法中的循环有点奇怪..。

代码语言:javascript
复制
public void run() {
    while(Main.mainThread.isAlive()){
        // ...
    }
}

我不确定成语是错的;我只会注意到,当我一直在寻找控制循环的最佳实践时,我从未见过对Thread.isAlive的调用。更常见的拼法

代码语言:javascript
复制
// run "forever", with internal breaks to shut things down
while (true) {
    ...
}

// Interruption equal Cancellation
while (! Thread.currentThread().interrupted()) {
    ...
}

// volatile state to track changes signalled by other threads
while (this.running) {
    ...
}

// more flexible, perhaps if there's a state machine somewhere
while (this.isRunning()) {
    ...
}

更广泛地说:

我一直在做一个很长的项目,试图了解多线程的基础知识。该应用程序应该模拟一个商店和一个共享一个Box和一个PayPal帐户的客户。

我觉得你还没说清楚。顾客和商店正在分享这两种资源。更具体地说,他们试图修改这两个资源。所以,您关心的锁是共享资源上的锁,而不是执行共享的资源上的锁。

通常的成语是这样的

代码语言:javascript
复制
// block until we can get exclusive control of the lock
synchronized (account) {

    // if we can't currently make progress... 
    while(insufficientFunds(account)) {

        // then release the lock, and block until something changes.
        account.wait ();
    }

    // Aha, we have control of the lock AND we know that there are sufficient
    // funds available

    debit(account);

    // we've made a change to the account balance, therefore we should
    // wake up the other threads that are blocking on it, in case they
    // care
    account.notifyAll();
} // release the lock

补充意见

CustomerShop可能不是Runnables;而是运行或参与任务的实体。如果将任务从实体中分离出来,事情可能会变得更加清晰。

代码语言:javascript
复制
public class Customer {
    public void scheduleOrders(ExecutorService executor) {
        Runnable task = new OrderBoxesTask(this.box, this.paypal);
        executor.submit(task);
    }
}

public OrderBoxesTask implements Runnable {
    public void run() {
        //...
    }
}

或者,也许不是CustomerShop,而是OrderingProcessFulfillmentProcess;“命名事物”是两个难题之一。

在你的例子中,大多数(全部?您的类正在操作静态变量,而不是实例变量。在这种情况下,您做了更多的打字,使您更难理解发生了什么。如果您使用封装数据的规则,编译器将帮助您显示您不了解需要做什么的地方。

考虑以下代码

代码语言:javascript
复制
Customer alice = new Customer(new Box(), new PayPalAcc(), "Alice");
Customer bob = new Customer(new Box(), new PayPalAcc(), "Bob");
System.out.println(alice.getName());

这不会像人们所期望的那样。这里的问题是,您指定了name (以及其他一切)是静态变量,这意味着Bob的名称是写在Alice上的。

在编写类时,应该假设所有状态/属性都属于实例,直到您知道其他情况为止。因此,变量声明不应该包含“静态”关键字。

代码语言:javascript
复制
public class Customer implements Runnable{

    private Box box;
    private PayPalAcc paypal;
    private String name;

还有:拼写你的单词PayPalAcc应该是PayPalAccountshopPP应该是shopPayPalshopPayPalAccountLock.

票数 5
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/115276

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档