我一直在做一个很长的项目,试图了解多线程的基础知识。该应用程序应该模拟一个商店和一个共享一个Box和一个PayPal帐户的客户。
Main.Java
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
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
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
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
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;
}
}发布于 2015-12-29 19:32:10
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,并让该服务为您控制线程管理。启动代码通常看起来像
// 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"));这将启动在后台运行的任务。当您想要取消任务的执行时,请关闭执行器。
// Not executor.shutdown(), which has a different purpose
executor.shutdownNow();shutdownNow将中断所有正在运行的线程。
在这样做时,我们暂时失去了控制线程名的能力。有一些方法可以配置执行器使用的ThreadFactory,但是它们不太适合您的用例。相反,你应该借用这个好主意:
mainThread.setName("mainThread");但是,与其将其应用于主线程(老实说,我不确定是否有大量的附加值),不如将其添加到run()方法的逻辑中
public class Customer implements Runnable{
@Override
public void run() {
Thread.currentThread().setName(this.getName());
// ...
}
}你在运行方法中的循环有点奇怪..。
public void run() {
while(Main.mainThread.isAlive()){
// ...
}
}我不确定成语是错的;我只会注意到,当我一直在寻找控制循环的最佳实践时,我从未见过对Thread.isAlive的调用。更常见的拼法
// 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帐户的客户。
我觉得你还没说清楚。顾客和商店正在分享这两种资源。更具体地说,他们试图修改这两个资源。所以,您关心的锁是共享资源上的锁,而不是执行共享的资源上的锁。
通常的成语是这样的
// 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补充意见
Customer和Shop可能不是Runnables;而是运行或参与任务的实体。如果将任务从实体中分离出来,事情可能会变得更加清晰。
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() {
//...
}
}或者,也许不是Customer和Shop,而是OrderingProcess和FulfillmentProcess;“命名事物”是两个难题之一。
在你的例子中,大多数(全部?您的类正在操作静态变量,而不是实例变量。在这种情况下,您做了更多的打字,使您更难理解发生了什么。如果您使用封装数据的规则,编译器将帮助您显示您不了解需要做什么的地方。
考虑以下代码
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上的。
在编写类时,应该假设所有状态/属性都属于实例,直到您知道其他情况为止。因此,变量声明不应该包含“静态”关键字。
public class Customer implements Runnable{
private Box box;
private PayPalAcc paypal;
private String name;还有:拼写你的单词PayPalAcc应该是PayPalAccount,shopPP应该是shopPayPal或shopPayPalAccountLock.
https://codereview.stackexchange.com/questions/115276
复制相似问题