【问题标题】:Running Threads One at a Time (Instead of Parallel)一次运行一个线程(而不是并行)
【发布时间】:2017-05-04 01:31:24
【问题描述】:

这与我之前提出的问题的代码相同,但解决了不同的问题。本质上,我正在尝试使用两个线程创建一个银行帐户,每个线程代表该帐户的用户。用户将从账户中存取 20 美元(随机)。

但是,这两个线程并行运行,并且提取/存款同时发生。我试图限制两个线程,使其在执行其之前等待另一个线程完成自己的运行方法。

下面列出的是代码。

线程创建类

import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

public class BankAccount extends Thread{
public static double balance = 1000;
public String threadName;

BankAccount(String name){
    threadName = name;
}
public void run(){
    System.out.println(threadName + "account initiated.");
    for(int i = 0; i < 10; i++){
        try{
            Random rand = new Random();
            int num = rand.nextInt(2) + 1;
            if(num == 1){
                Thread.sleep(200); //0.2 seconds to deposit
                System.out.println(threadName + " is depositing 20$ in the bank.");
                balance += 20;
                System.out.println("The new balance is " + balance + "dollars" );
            }
            else{
                Thread.sleep(500); //half a second to withdraw
                System.out.println(threadName + " is withdrawing 20$ from the bank.");
                balance -= 20;
                System.out.println("The new balance is " + balance + "dollars.");
                }
        }
        catch(InterruptedException e){
            System.out.println("Process terminated.");
            }
        }
    }
}

线程驱动类

public class BankAccountSimDriver {
    public static void main(String[] args){
    Thread user1 = new BankAccountSIm("user1"); 
    Thread user2 = new BankAccountSIm("user2");

    user1.start();
    user2.start();
    }
}

当前输出:

user1 initiated.
user2 initiated.
user1 is depositing 20$ in the bank.
user2 is depositing 20$ in the bank.
The new balance is 1020.0 dollars
The new balance is 1040.0 dollars
user2 is depositing 20$ in the bank.
The new balance is 1060.0 dollars
user1 is withdrawing 20$ from the bank.
The new balance is 1040.0 dollars.

目前,user1 和 user2 同时运行。我想编辑代码,以便一次只有一个用户可以存款/取款(由 sleep() 时间分隔表示)

所以理想的输出:

user1 initiated.
//wait 0.2 seconds
user1 is depositing 20$ in the bank.
The new balance is 1020.0 dollars
user2 initiated.
//wait 0.2 seconds
user2 is depositing 20$ in the bank.
The new balance is 1040.0 dollars
//wait 0.5 seconds
user1 is withdrawing 20$ from the bank.
The new balance is 1020.0 dollars.
...

【问题讨论】:

  • 如果不想让线程同时运行,为什么还要使用线程?
  • 你是在一个线程中根据随机值进行存款和取款。那你为什么要启动第二个线程 user2.start()
  • 我使用线程来模拟两个不同的人在一个帐户上执行操作,一次一个。有没有更简单或更有效的替代方案? (如您所知,Java 新手)
  • 不要在每次使用时实例化您的Random 实例。这扼杀了它的“随机”特性。实例化一次,让它处理生成新值。
  • 您可以考虑在代码的关键部分周围使用Java 的许多同步机制之一。您是否阅读过任何有关 Java 并发编程的教程、书籍或其他培训材料?我猜不是因为缺乏同步和滥用InterruptedException

标签: java multithreading


【解决方案1】:

你需要的是管理对balance的并发访问,而不是序列化线程,至少你需要序列化对balance的访问。有很多方法可以解决它。基本上,您需要一个,只允许一个线程修改balance,另一个线程被阻塞,直到另一个线程释放锁。

首先你需要将BanckAccount和它的用户分开。

class BankAccount { ... }

class User implements Runnable {
    private BankAccount account;
    public User(BankAccount account) {
         this.account = account;
    }
    public void run() {
      // do something...
    }
}

class Test {
    public static void main(String []a) {
        // One account
        BankAccount a = new BankAccount();
        // Shared by 2 users
        User user1 = new User(a);
        User user2 = new User(a);
        // Make users behave concurrently
        Thread t1 = new Thread(user1);
        Thread t2 = new Thread(user2);
        t1.start();
        t2.start();
        // Wait 'til the end of the users activity
        t1.join();
        t2.join();
    }
}

现在如何管理用户对同一银行账户的并发访问?您可以通过使BankAccount 方法synchronized 使用BankAccount 对象作为隐式锁:

class BankAccount {
    private int balance;
    public BankAccount(int initialBalance) {
        balance = initialBalance;
    }
    public synchronized int deposit(int amount) {
        balance += amount;
        return balance;
    }
    public synchronized int withdraw(int amount) {
        balance -= amount;
        return balance;
    }
}

这样做可以确保没有两个线程可以同时在标记为synchronized的方法中,因此没有两个线程可以同时修改余额。

但是...(并发会导致微妙的问题)...每个线程都可能使用balance 的缓存副本,因此通常将该字段标记为volatile 更合适:

private volatile int balance;

请注意,在这种简单的情况下,有些事情可能会被简化,但我试图向您展示存在哪些问题以及如何解决这些问题。另请注意,即使此代码也可能导致奇怪的输出,但可以保证余额是正确的(现在挖掘并发性)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-08
    • 2019-06-09
    • 1970-01-01
    • 2021-04-20
    相关资源
    最近更新 更多