Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 简单现金存取程序中的多线程处理_Java_Multithreading_Synchronized - Fatal编程技术网

Java 简单现金存取程序中的多线程处理

Java 简单现金存取程序中的多线程处理,java,multithreading,synchronized,Java,Multithreading,Synchronized,我的导师说使用多线程更新帐户管理系统。下面给出了系统的大致概念。 这是我的源代码 科目类别 public class Account { int balance= 1000; public int getBal(){ return balance; } public void withdraw(int bal){ balance= balance-bal; } public void deposit(int

我的导师说使用多线程更新帐户管理系统。下面给出了系统的大致概念。

这是我的源代码

科目类别

public class Account {
    int balance= 1000;

    public int getBal(){
        return balance;
    }

    public void withdraw(int bal){
        balance= balance-bal;
    }

    public void deposit(int bal){
        balance= balance+bal;
    }
}
public class ThreadExercise implements Runnable{

    Account acc = new Account();

    public static void main(String[] args) {
        ThreadExercise ts = new ThreadExercise();
        Thread t1 = new Thread(ts, "person 1");
        Thread t2 = new Thread(ts, "person 2");
        Thread t3 = new Thread(ts, "person 3");
        t1.start();
        t2.start();
        t3.start();
    }

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            makeWithdraw(100);
            if (acc.getBal() < 0) {
                System.out.println("account is overdrawn!");
            }
            deposit(200);
        }
    }


    private synchronized void makeWithdraw(int bal){
        if (acc.getBal()>=bal) {
            System.out.println(Thread.currentThread().getName()+" "+ "is try to withdraw");
            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
            acc.withdraw(bal);
            System.out.println(Thread.currentThread().getName()+" "+ "is complete the withdraw");
        }else{        
            System.out.println(Thread.currentThread().getName()+ " "+"doesn't have enough money for withdraw ");
        }
    }

    private synchronized void deposit(int bal){
        if (bal>0) {
            System.out.println(Thread.currentThread().getName()+" "+ " is try to deposit");
            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
            acc.deposit(bal);
            System.out.println(Thread.currentThread().getName()+" "+ "is complete the deposit");
        }else{        
            System.out.println(Thread.currentThread().getName()+ " "+"doesn't have enough money for deposit");
        }
    }
}
ThreadExercise类

public class Account {
    int balance= 1000;

    public int getBal(){
        return balance;
    }

    public void withdraw(int bal){
        balance= balance-bal;
    }

    public void deposit(int bal){
        balance= balance+bal;
    }
}
public class ThreadExercise implements Runnable{

    Account acc = new Account();

    public static void main(String[] args) {
        ThreadExercise ts = new ThreadExercise();
        Thread t1 = new Thread(ts, "person 1");
        Thread t2 = new Thread(ts, "person 2");
        Thread t3 = new Thread(ts, "person 3");
        t1.start();
        t2.start();
        t3.start();
    }

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            makeWithdraw(100);
            if (acc.getBal() < 0) {
                System.out.println("account is overdrawn!");
            }
            deposit(200);
        }
    }


    private synchronized void makeWithdraw(int bal){
        if (acc.getBal()>=bal) {
            System.out.println(Thread.currentThread().getName()+" "+ "is try to withdraw");
            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
            acc.withdraw(bal);
            System.out.println(Thread.currentThread().getName()+" "+ "is complete the withdraw");
        }else{        
            System.out.println(Thread.currentThread().getName()+ " "+"doesn't have enough money for withdraw ");
        }
    }

    private synchronized void deposit(int bal){
        if (bal>0) {
            System.out.println(Thread.currentThread().getName()+" "+ " is try to deposit");
            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
            acc.deposit(bal);
            System.out.println(Thread.currentThread().getName()+" "+ "is complete the deposit");
        }else{        
            System.out.println(Thread.currentThread().getName()+ " "+"doesn't have enough money for deposit");
        }
    }
}
public类ThreadExercise实现可运行{
账户acc=新账户();
公共静态void main(字符串[]args){
ThreadExercise ts=新的ThreadExercise();
线程t1=新线程(ts,“人1”);
螺纹t2=新螺纹(ts,“人员2”);
螺纹t3=新螺纹(ts,“人员3”);
t1.start();
t2.start();
t3.start();
}
@凌驾
公开募捐{
对于(int i=0;i<3;i++){
makedraw(100);
如果(根据getBal()<0){
System.out.println(“帐户透支!”);
}
存款(200);
}
}
专用同步void makedraw(int-bal){
如果(根据getBal()>=bal){
System.out.println(Thread.currentThread().getName()+“”+“正在尝试撤消”);
试一试{
睡眠(100);
}捕获(例外e){
e、 printStackTrace();
}
acc.draw(bal);
System.out.println(Thread.currentThread().getName()+“”+“完成撤消”);
}否则{
System.out.println(Thread.currentThread().getName()+“”+“没有足够的钱取款”);
}
}
私人同步作废存款(整数余额){
如果(bal>0){
System.out.println(Thread.currentThread().getName()+“”+“正在尝试存放”);
试一试{
睡眠(100);
}捕获(例外e){
e、 printStackTrace();
}
账户存款(bal);
System.out.println(Thread.currentThread().getName()+“”+“完成存款”);
}否则{
System.out.println(Thread.currentThread().getName()+“”+”没有足够的存款);
}
}
}
代码运行良好。但我真的认为这段代码缺失了一些东西。你能帮我找出那个毛病吗


在ThreadExecute类中同步makeDraw()和deposit()方法还不够吗?我是否应该删除该同步并同步Account类中的Draw()和deposit()。请给我一个清楚的想法。

谢谢你的支持

考虑到设计,Account类必须同步(其中的方法)

目前,其他人可能会将实例检索到帐户,并以线程不安全的方式使用它。在这种情况下,只需从其他地方调用Account的方法就会使它崩溃

public class Account {
  private final Object lock = new Object();
  // Must be private to be thread-safe!
  private int balance= 1000;

  public int getBal(){
    return balance;
  }

  public synchronized void withdraw(int bal){
    synchronized (lock) {
      balance= balance-bal;
    }
  }

  public synchronized void deposit(int bal){
    synchronized (lock) {
      balance= balance+bal;
    }
  }
}

您的帐户类不是线程安全的。虽然您已经同步了ThreadExecution类的存款和取款方法,但存款/取款锁定线程时,可以更改基础余额

考虑场景

线程1调用ThreadExercise.deposit,它检查余额并等待。 线程2同时唤醒并更新余额

因此,您的帐户余额实际上并没有与并发的存款+取款通话同步

您可以如下定义余额

 AtomicInteger balance = new AtomicInteger(1000);
那么提取方法可以写如下

public boolean withdraw (int amtToWithdraw, int existingBalance){
    return balance.compareAndSet(existingBalance,existingBalance-amtToWithdraw); 
}

public void deposit(int amtToDeposit, int existingBalance){
    return balance.compareAndSet(existingBalance,existingBalance+amtToDeposit);
}

您可能需要处理故障情况。

我不确定其他答案是否清楚

您已经同步了
ThreadExercise
类上的方法。 这意味着一次只能有一个线程在给定的
ThreadExercise
对象上调用这些方法。 这没有效果,因为每个线程对象无论如何只会调用一个这样的对象上的方法

您需要同步
Account
类的方法,以确保一次只有一个线程在任何给定的
Account
上调用一个方法

当然,在任何实际系统中,
Account
对象都会(以某种方式)序列化到某个数据库或对象存储中,并且您需要确保您的应用程序没有引入表示一个“物理”帐户的两个“doppelganger”对象。在具有多个ATM交换机的分布式系统上,这可能很棘手

如果引入平衡转移的概念,则可能需要引入进一步的同步。如果某个观察员进程无法接受以下情况,则尤其如此:

Account 1: $100
Account 2: $0

Account 1: $40
Account 2: $0

Account 1: $40
Account 2: $60
其中一笔60美元的转账消失并重新出现。 那里没有业务问题。银行从坐在上面的X那里拿钱,然后把钱传给Y,赚了数百万,这和他们榨取客户的钱没有什么好的理由。 我只是想指出,向方法添加
synchronized
并不是并发编程的全部答案

我曾经看到一个组织,成功地执行了这样一种转移,在中间有错误,使帐户处于状态:

Account 1: $100
Account 2: $60
从帐户1到帐户2的60美元(1000万美元IRL)到达,但从未离开!那是另一个故事

然而,要回答这个问题:

public class Account {
    int balance= 1000;

    public int getBal(){
        return balance;
    }

    public synchronized void withdraw(int bal){
        balance= balance-bal;
    }

    public synchronized void deposit(int bal){
        balance= balance+bal;
    }
}
我挑衅地没有同步
getBal()
。一方面,
int
读取和写入是原子的,因此它总是读取一致的
balance
值,而不是(比如)写入操作只更新低字节的“混蛋”。如果将其更改为
long
,JVM将不再保证这一点

但是,不同步意味着您可以看到异常位置:

  Account 1: $100
  Account 2: $60
即使您的代码是:

account1.withdraw(60);
account2.deposit(60);
这是因为同步不仅会引入阻塞,还会影响内存屏障。假设一个单独的线程缓存了
account1
,但没有
account2
,如果没有同步,它不会知道
account1
已过时,但是
public class ThreadExercise extends Thread implements Runnable {

private Person person;

public ThreadExercise(Person p) {
    this.person = p;
}

public static void main(String[] args) {

    ThreadExercise ts1 = new ThreadExercise(new Person("person 1"));
    ts1.start();
    ThreadExercise ts2 = new ThreadExercise(new Person("person 2"));
    ts2.start();
    ThreadExercise ts3 = new ThreadExercise(new Person("person 3"));
    ts3.start();

}

@Override
public void run() {
    for (int i = 0; i < 3; i++) {
        try {
            Account acc = Account.getAccount(person);
            acc.withdraw(100);
            try {
                Thread.sleep(200);
            } catch (InterruptedException ex) {
                Logger.getLogger(ThreadExercise.class.getName()).log(Level.SEVERE, null, ex);
            }
            if (acc.getBal() < 0) {
                System.out.println("account is overdrawn!");
            }
            acc.deposit(200);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    System.out.println("Final Acc balance is Rs." + Account.getBal());
}}