在保护写操作的情况下获得竞争条件-Java
使用java.util.concurrent.locks.ReentrantLock库,如下所示:在保护写操作的情况下获得竞争条件-Java,java,multithreading,concurrency,synchronization,Java,Multithreading,Concurrency,Synchronization,使用java.util.concurrent.locks.ReentrantLock库,如下所示: package osproj221; import java.util.Random; public class RaceThread implements Runnable { private Random myRand = new Random(); private int counter = 0; private Accounts accounts; p
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());
}
}
两个线程生成一个随机数,并使用它来更新存储在类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());
}
}
最后是我的主课
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条件,而另一个线程无限期地继续。我糊涂了 同步您的写入是不够的;您还需要使用相同的锁/监视器同步读取 如果你不这样做,坏事就会发生:
- 不能保证一个线程上的写操作对另一个线程上的未同步读取器是可见的
- 即使写操作是可见的,也不能保证它们将按预期顺序可见,或者两次非同步读取将看到不同变量的一致值。您很可能看到旧值
和新值account1
,反之亦然account2
- 不能保证一个线程上的写操作对另一个线程上的未同步读取器是可见的
- 即使写操作是可见的,也不能保证它们将按预期顺序可见,或者两次非同步读取将看到不同变量的一致值。您很可能看到旧值
和新值account1
,反之亦然account2
- 线程1:
- 更新计数
- 锁
- account1+=r
- 线程2:
- getAccount1
- getAccount2
- account1+account2!=0
- 线程1:
- account2-=r
- 解锁
- 线程1:
- 更新计数
- 锁
- account1+=r
- 线程2:
- getAccount1
- getAccount2
- account1+account2!=0
- 线程1:
- account2-=r
- 解锁
- 同步您的写入是不够的;您还需要使用相同的锁/监视器同步读取
如果你不这样做,坏事就会发生:
因此,有可能一个线程将account1设置为新值,而另一个线程正在读取旧account2。您只是在同步对变量的写入访问
因此,有可能一个线程将account1设置为新值,而另一个线程正在读取旧account2。您的getter未被锁定。因此,即使您在更新时使用了互斥锁,另一个线程仍在调用可以不锁定运行的getter
- 这是因为您没有锁定getter上的读取。这意味着线程2可以在不一致的状态下读取数据,而线程1正在更新数据(介于+=和-=)。
问题可能发生如下情况:
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());
}
}
线程1:
public int getAccountsSum() {
try {
mutex.lock();
return this.account1 + this.account2;
} finally { mutex.unlock(); }
并在RaceThread.run()中将while条件更改为:
}while((accounts.getAccountsSum() == 0));
这是因为您没有锁定getter上的读取。这意味着线程2可以在不一致的状态下读取数据,而线程1正在更新数据(介于+=和-=)。
问题可能发生如下情况:
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());
}
}
线程1:
更新计数(5)李>
获得锁
account1+=5;->account1=0+5;->account1=5李>
线程2:
getAccount1()account2=0-5->account2=-5李>
松开锁
解决方案:
不幸的是,您不能简单地单独同步getter,相反,我建议您使用一种新的get方法:
public int getAccountsSum() {
try {
mutex.lock();
return this.account1 + this.account2;
} finally { mutex.unlock(); }
并在RaceThread.run()中将while条件更改为:
}while((accounts.getAccountsSum() == 0));