Akka STM –与STM Ref和Agent进行乒乓球比赛

乒乓是一个经典示例,其中2个玩家(或线程)访问共享资源–乒乓球桌并在彼此之间传递Ball(状态变量)。 使用任何共享资源,除非我们同步访问,否则线程可能会遇到潜在的死锁情况。
Akka STM –与STM Ref和Agent进行乒乓球比赛

PingPong算法非常简单

如果轮到我{
更新下一轮
ping / pong-记录点击
通知其他线程
}其他{
等待通知
}

让我们来看一个例子,看看它是如何工作的! 这是我们的Player类,它实现Runnable并接受对共享资源和消息的访问

  

 
 
 public class Player implements Runnable {
  PingPong myTable;  Table where they play
  String myOpponent;
 
  public Player(String opponent, PingPong table) {
   myTable = table;
   myOpponent = opponent;
  }
 
  public void run() {
   while (myTable.hit(myOpponent))
    ;
  }

 }

其次,我们看到PingPong表类,该类具有一个同步方法hit(),无论是否轮到我,都在其中进行检查。 如果轮到我了,请记录ping并更新对手姓名的共享变量。

  
 
 public class PingPong {
   state variable identifying whose turn it is.
  private String whoseTurn = null;
 
  public synchronized boolean hit(String opponent) {
 
   String x = Thread.currentThread().getName();
 
   if (x.compareTo(whoseTurn) == 0) {
    System.out.println('PING! (' + x + ')');
    whoseTurn = opponent;
    notifyAll();
   } else {
   try {  wait(2500); } 
   catch (InterruptedException e) { }
   }
   }
 }

接下来,我们开始游戏并使玩家开始!

  

 
 
 public class Game {
  public static void main(String args[]) {
   PingPong table = new PingPong();
   Thread alice = new Thread(new Player('bob', table));
   Thread bob = new Thread(new Player('alice', table));
 
   alice.setName('alice');
   bob.setName('bob');
   alice.start();  alice starts playing
   bob.start();  bob starts playing
   try {
     Wait 5 seconds
    Thread.sleep(5000);
   } catch (InterruptedException e) {
   }
 
   table.hit('DONE');  cause the players to quit their threads.
   try {
    Thread.sleep(100);
   } catch (InterruptedException e) {
   }
  }
 }

就是这样,我们正在运行PingPong游戏。 在这种情况下,我们看到了同步方法hit()如何仅允许一个线程访问共享资源– itsTurn。

Akka STM提供了两个构造Refs和Agents。 引用(事务引用)提供对多个身份的协调同步访问。 代理提供对单个身份的非协调异步访问。

参考

在我们的例子中,由于共享状态变量是单个标识,因此引用的使用是过大的,但是我们仍然会继续查看它们的用法。

  

 
 
 public class PingPong {
 
  updates to Ref.View are synchronous
  Ref.View<String> whoseTurn;
 
  public PingPong(Ref.View<String> player) {
   whoseTurn = player;
  }
 
  public boolean hit(final String opponent) {
   final String x = Thread.currentThread().getName();
 
   if (x.compareTo(whoseTurn.get()) == 0) {
    System.out.println('PING! (' + x + ')');
    whoseTurn.set(opponent);
   } else {
    try {
     wait(2500);
           } catch (Exception e) {
    }
   }
 }
 }

这里的关键是以下

  • 同步关键字丢失
  • 将状态变量定义为Ref
    //更新到Ref.View是同步的
    Ref.View <string>其Turn;
  • 调用更新Ref是协调和同步的
    whoTurn.set(opponent) ;

因此,当我们使用Ref保持状态时,对Ref的访问会在事务中自动同步。

代理商

由于代理提供了不协调的异步访问,因此使用代理进行状态操作将意味着我们需要等到所有更新都已应用到代理之后。 代理为获取提供非阻塞访问。

  

 
 
 public class PingPong {
 
  Agent<String> whoseTurn;
 
  public PingPong(Agent<String> player) {
   whoseTurn = player;
  }
 
  public boolean hit(final String opponent) {
   final String x = Thread.currentThread().getName();
 
   wait till all the messages are processed to make 
   you get the correct value, as updated to Agents are
   async
   String result = whoseTurn.await(new Timeout(5, SECONDS));
 
   if (x.compareTo(result) == 0) {
    System.out.println('PING! (' + x + ')');
    whoseTurn.send(opponent);
   } else {
    try {
     wait(2500);
 
    } catch (Exception e) {
    }
   }
   return true;  keep playing.
  }
 }

这里的关键是以下

  • 同步关键字丢失
  • 将状态变量定义为Agent
    //更新到Ref.View是同步的
    Agent <string>其Turnn;
  • 等待对代理的更新,因为对代理的更新是异步的
    字符串结果= whoTurn.await(new Timeout(5,SECONDS));
  • 调用更新Ref是协调和同步的
    whoTurn.send(opponent) ;

这些示例中引用的所有代码都可以在– https://github.com/write2munish/Akka-Essentials/tree/master/AkkaSTMExample/src/main/java/org/akka/essentials/stm/pingpong中找到
示例1 –用于基于普通线程的同步
示例2 –使用Refs进行同步
示例3 –使用代理进行同步

参考:Akka Essentials博客上,与我们的JCG合作伙伴 Munish K Gupta 一起使用STM – Refs和Agents打乒乓球。

翻译自: https://www.javacodegeeks.com/2012/05/akka-stm-playing-pingpong-with-stm-refs.html