尽管保护写操作仍获取竞争条件 - Java

发布于 2024-11-30 20:51:28 字数 2234 浏览 0 评论 0原文

使用 java.util.concurrent.locks.ReentrantLock 库如下:

两个线程生成一个随机数,并用它来更新存储在类 Accounts 中的共享变量 account1 和 account2 - 锁用于保护对共享变量的

package osproj221;
import java.util.concurrent.locks.ReentrantLock;

public class Accounts {
    private int account1,account2;
    private final ReentrantLock mutex;

    public Accounts(){
        account1=account2=0;
        mutex = new ReentrantLock();
    }

    public void updateAccounts(int amt){
        try{
            mutex.lock();
            account1 += amt;
            account2 -= amt;
        }catch(Exception ex){
            System.out.println(ex);
        }finally{mutex.unlock();}
    }

    public int getAccount1(){
        return this.account1;
    }

    public int getAccount2(){
        return this.account2;
    }
}

写入线程实现 Runnable 接口如下:

package osproj221;
import java.util.Random;

public class RaceThread implements Runnable {

    private Random myRand = new Random();
    private int counter = 0;
    private Accounts accounts;

    public RaceThread(Accounts accounts){
        this.accounts = accounts;
    }

    public void run(){

        do{
            int r = myRand.nextInt(300);
            r = Math.abs(r);
            accounts.updateAccounts(r);
            counter++;
        }while((accounts.getAccount1() + accounts.getAccount2() == 0));

        System.out.println(counter + " " + accounts.getAccount1() + " " + accounts.getAccount2());
    }

}

最后我的 Main 类

package osproj221;

public class Main {

    public static void main(String[] args) {
        Accounts myAccounts = new Accounts();

        Thread t1 = new Thread(new RaceThread(myAccounts));
        Thread t2 = new Thread(new RaceThread(myAccounts));

        t1.start();
        t2.start();

        try{
            t1.join();
            t2.join();
        }catch(Exception ex){
            System.out.println(ex);
        }

        System.out.println(myAccounts.getAccount1() + " " + myAccounts.getAccount2());

    }

}

看起来比我想象的要长一些 - 抱歉。我希望两个线程都不会终止,因为 account1+account2 应始终 = 0,因为互斥体负责保护 account1 和 account2 的更新。似乎发生的情况是,其中一个线程退出,因为它未满足 account1+account2==0 条件,而另一个线程无限期地继续。我很困惑!

Using the java.util.concurrent.locks.ReentrantLock library as follows:

Two threads generate a random number and use it to update the shared variables account1 and account2 stored in class Accounts - a lock is used to protect the writing to the shared vars

package osproj221;
import java.util.concurrent.locks.ReentrantLock;

public class Accounts {
    private int account1,account2;
    private final ReentrantLock mutex;

    public Accounts(){
        account1=account2=0;
        mutex = new ReentrantLock();
    }

    public void updateAccounts(int amt){
        try{
            mutex.lock();
            account1 += amt;
            account2 -= amt;
        }catch(Exception ex){
            System.out.println(ex);
        }finally{mutex.unlock();}
    }

    public int getAccount1(){
        return this.account1;
    }

    public int getAccount2(){
        return this.account2;
    }
}

My threads implement the Runnable interface as follows:

package osproj221;
import java.util.Random;

public class RaceThread implements Runnable {

    private Random myRand = new Random();
    private int counter = 0;
    private Accounts accounts;

    public RaceThread(Accounts accounts){
        this.accounts = accounts;
    }

    public void run(){

        do{
            int r = myRand.nextInt(300);
            r = Math.abs(r);
            accounts.updateAccounts(r);
            counter++;
        }while((accounts.getAccount1() + accounts.getAccount2() == 0));

        System.out.println(counter + " " + accounts.getAccount1() + " " + accounts.getAccount2());
    }

}

and finally my Main class

package osproj221;

public class Main {

    public static void main(String[] args) {
        Accounts myAccounts = new Accounts();

        Thread t1 = new Thread(new RaceThread(myAccounts));
        Thread t2 = new Thread(new RaceThread(myAccounts));

        t1.start();
        t2.start();

        try{
            t1.join();
            t2.join();
        }catch(Exception ex){
            System.out.println(ex);
        }

        System.out.println(myAccounts.getAccount1() + " " + myAccounts.getAccount2());

    }

}

that looks a little longer than i thought it would - apologies. I would expect that neither of the threads would terminate, because account1+account2 should always = 0 as the mutex handles protecting the updating of account1 and account2. What seems to happen is that one of the threads exits because it fails the account1+account2==0 condition, and the other one continues indefinitely. im confused!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

尐偏执 2024-12-07 20:51:28

这是因为你没有锁定吸气剂上的读取。这意味着线程 2 可以读取不一致状态的数据,而线程 1 正在更新数据(在 += 和 -= 之间)。
该问题可能发生如下:

线程 1:

  1. updateAccounts(5);
  2. 获取锁定
  3. 账户1 += 5; ->账户1 = 0 + 5; ->账户1 = 5;

线程 2:

  1. getAccount1() <- 返回 5
  2. getAccount2() <- 返回 0
  3. 5+0 != 0 -->退出

线程 1:

  1. account2 -= 5 ->账户2 = 0-5 ->账户2 = -5;
  2. 释放锁定。

解决方案:
不幸的是,您不能简单地单独同步您的 getter,我建议使用一种新的 get 方法:

public int getAccountsSum() {
    try {
        mutex.lock();
        return this.account1 + this.account2;
    } finally { mutex.unlock(); }

并在 RaceThread.run() 中将 while 条件更改为:

    }while((accounts.getAccountsSum() == 0));

It's because you're not locking the reads on your getters. This means thread 2 can read the data in an inconsistent state, while thread 1 is updating the data (between += and -=).
The problem can happen as follows:

Thread 1:

  1. updateAccounts(5);
  2. OBTAIN LOCK
  3. account1 += 5; -> account1 = 0 + 5; -> account1 = 5;

Thread 2:

  1. getAccount1() <- returns 5
  2. getAccount2() <- returns 0
  3. 5+0 != 0 --> EXIT

Thread 1:

  1. account2 -= 5 -> account2 = 0-5 -> account2 = -5;
  2. RELEASE LOCK.

The solution:
Unfortunately you can't simply synchonize your getters individually I would instead recommend a new get method:

public int getAccountsSum() {
    try {
        mutex.lock();
        return this.account1 + this.account2;
    } finally { mutex.unlock(); }

and in RaceThread.run() change the while condition to:

    }while((accounts.getAccountsSum() == 0));
彡翼 2024-12-07 20:51:28

仅仅同步你的写入是不够的;您还需要使用相同的锁/监视器来同步读取。

如果不这样做,可能会发生不好的事情:

  • 无法保证一个线程上的写入永远对于另一个线程上的不同步读取器可见。
  • 即使写入可见,也不能保证它们将以预期的顺序变得可见,或者两个不同步的读取将看到不同变量的一致值。您很可能会看到 account1 的旧值和 account2 的新值,反之亦然。

It's not enough to synchronize your writes; you also need to synchronize your reads, using the same lock/monitor.

If you don't, bad things can happen:

  • There is no guarantee that a write on one thread will ever be visible to an unsynchronized reader on another thread.
  • Even if the writes are visible, there is no guarantee that they will become visible in the expected order, or that your two unsynchronized reads will see coherent values for the different variables. It's quite possible for you to see an old value of account1 and a new value of account2, or vice-versa.
作死小能手 2024-12-07 20:51:28

您仅同步对变量的写访问。

因此,有可能一个线程将 account1 设置为新值,而另一个线程正在读取旧的 account2。

you are only syncronizing the write access to the variables.

so there is a chance of one thread setting account1 to a new value while the other is reading an old account2.

野の 2024-12-07 20:51:28

您的 Getter 未锁定。因此,即使您在更新时使用互斥体,另一个线程也会调用可以解锁运行的 Getters。

  • 线程 1:
    • 更新帐户
      • 锁定
      • 账户1 += r
  • 线程2:
    • 获取帐户1
    • 获取帐户2
    • 账户1 + 账户2 != 0
  • 线程1:
      • 帐户2 -= r
      • 解锁

    公共 int getAccounts(){
    整数结果 = 0;
    尝试{
    互斥体.lock();
    结果=账户1+账户2;
    }catch(异常前){
    System.out.println(ex);
    }最后{mutex.unlock();}
    返回结果;
    }

Your Getters are not locked. So even though you use a mutex on updating the other thread is calling the Getters which can run unlocked.

  • Thread1:
    • updateAccounts
      • LOCK
      • account1 += r
  • Thread2:
    • getAccount1
    • getAccount2
    • account1 + account2 != 0
  • Thread1:
      • account2 -= r
      • UNLOCK

    public int getAccounts(){
    int result = 0;
    try{
    mutex.lock();
    result = account1 + account2;
    }catch(Exception ex){
    System.out.println(ex);
    }finally{mutex.unlock();}
    return result;
    }

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文