使用正确的同步方式避免数据丢失

Using the correct way to Synchronized to avoid data loss

我正在尝试创建一个多线程的 ATM 系统,以避免提款请求的数据丢失。

我创建了 2 个线程,每个线程都试图从银行余额中提取 1000-5000 钱。

我不希望线程一起工作,我希望它们中的每一个都等待第二个线程完成工作。 当余额为0,或者取款请求高于当前余额时,两个Thread都应该停止取款。

我的问题是我看到线程 1 运行 4 次立即让线程 2 进入,以此类推线程 2,它们似乎没有等待彼此进入动作。

主要:

class Main {
    public static void main(String args[]) {
        Bank bank = new Bank(100000);
        ThreadSync threadSync = new ThreadSync();
        Thread client1 = new ClientThread(bank, threadSync,1);
        Thread client2 = new ClientThread(bank, threadSync,2);


        client1.start();
        client2.start();
    }
}

银行:

public class Bank {
    private int balance;
    public Bank(int balance) {
        this.balance = balance;
    }
    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }
}

客户端线程:

public class ClientThread extends Thread {
    private Bank bank;
    private ThreadSync threadSync;
    private int client;

    ClientThread(Bank bank, ThreadSync threadSync, int client) {
        this.bank = bank;
        this.threadSync = threadSync;
        this.client = client;
    }

    @Override
    public void run() {

        int randomNumber;
        while (bank.getBalance() > 0) {
            synchronized (threadSync) {
                randomNumber = ((int) (Math.random() * (5000 - 1000 + 1)) + 1000);
                try {
                    if (bank.getBalance() - randomNumber >= 0) {
                        bank.setBalance(bank.getBalance() - randomNumber);
                    }
                    System.out.println(client + " - " + bank.getBalance() + " withdrawed - " + randomNumber);
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

线程同步:

public class ThreadSync {
    public void send(String msg)
    {
        System.out.println("Sending\t"  + msg );
        try
        {
            Thread.sleep(1000);
        }
        catch (Exception e)
        {
            System.out.println("Thread  interrupted.");
        }
        System.out.println("\n" + msg + "Sent");
    }
}

谢谢!

不是一个完整的答案,但你应该知道这是一个错误的模式:

while (...trivial test...) {
    synchronized(lock) {
        ...something that takes some time...
    }
}

问题是每次循环释放锁时,几乎接下来它所做的就是再次重新锁定它。如果线程 A 进入循环,然后线程 B 随后 尝试 进入,则线程 B 很可能会 starved.

当线程B试图进入synchronized块时,会swapped out(a.k.a.,"blocked","put to sleep" ) 由操作系统。

当线程 A 释放锁时,OS 需要时间再次换回线程 B。同时,线程 A 已经有了 运行ning 的领先优势:它重新锁定锁,重新开始工作,当线程 B 准备 运行 时,已经太晚了。 OS 将再次换出 B,下一次迭代可能会发生同样的事情,下一次,...


诀窍是重新构造您的程序,以便线程永远不需要将锁保持锁定很长一段时间。这听起来可能很矛盾,但有时您可以通过让线程执行 额外的 工作来加速并行计算,前提是额外的工作能够使它们相互避开。

如果您不能重新构建程序,使大部分实际工作发生在关键部分(即“锁定”部分)之外,那么这可能表明您遇到了问题试图解决不太适合多线程解决方案。