移动 > 游戏 > 手游

一行Java代码实现游戏中交换装备

128人参与 2024-08-04 手游

本文分享自华为云社区《一行java代码实现两玩家交换装备【并发编程】》,作者:陈皮的javalib 。

1 exchanger 是什么

jdk 1.5 开始 juc 包下提供的 exchanger 类可用于两个线程之间交换信息。exchanger 对象可理解为一个包含2个格子的容器,通过调用 exchanger 方法向其中的格子填充信息,当两个格子中的均被填充信息时,自动交换两个格子中的信息,然后将交换的信息返回给调用线程,从而实现两个线程的信息交换。

功能看似简单,但这在某些场景下是很有用处的,例如游戏中两个玩家交换装备;交友软件男女心仪对象匹配。

下面简单模拟下两个玩家交换装备的场景。

package com.chenpi;

import java.util.concurrent.exchanger;

/**
 * @description
 * @author 陈皮
 * @date 2021/7/11
 * @version 1.0
 */
public class chenpimain {

  public static void main(string[] args) throws interruptedexception {

    exchanger<string> exchanger = new exchanger<>();

    new thread(() -> {
      string str = null;
      try {
        str = exchanger.exchange("屠龙刀");
      } catch (interruptedexception e) {
        e.printstacktrace();
      }
      system.out.println("交易成功," + thread.currentthread().getname() + "获得" + str);
    }, "周芷若").start();

    new thread(() -> {
      string str = null;
      try {
        str = exchanger.exchange("倚天剑");
      } catch (interruptedexception e) {
        e.printstacktrace();
      }
      system.out.println("交易成功," + thread.currentthread().getname() + "获得" + str);
    }, "张无忌").start();
  }
}

// 输出结果如下
交易成功,张无忌获得屠龙刀
交易成功,周芷若获得倚天剑

2 exchanger 详解

exchager 类可用于两个线程之间交换信息,如果一个线程调用了 exchanger 对象的 exchange 方法之后,会一直阻塞直到另一个线程来和它交换信息,交换之后的信息返回给调用线程,从而实现两个线程的信息交换。

exchager 底层也是使用到了自旋和 cas 机制。

注意,如果超过两个线程调用同一个 exchanger 对象 exchange 方法时,结果是不可预计的,只要有2个线程满足条件了,就认为匹配成功并交换信息。而剩下的未能得到配对的线程,则会被阻塞一直等待直到有另一个线程能与它匹配与之配对。

package com.chenpi;

import java.util.concurrent.exchanger;

/**
 * @description
 * @author 陈皮
 * @date 2021/7/11
 * @version 1.0
 */
public class chenpimain {

  public static void main(string[] args) {

    exchanger<string> exchanger = new exchanger<>();

    new thread(() -> {
      string str = null;
      try {
        str = exchanger.exchange("屠龙刀");
      } catch (interruptedexception e) {
        e.printstacktrace();
      }
      system.out.println("交易成功," + thread.currentthread().getname() + "获得" + str);
    }, "周芷若").start();

    new thread(() -> {
      string str = null;
      try {
        str = exchanger.exchange("倚天剑");
      } catch (interruptedexception e) {
        e.printstacktrace();
      }
      system.out.println("交易成功," + thread.currentthread().getname() + "获得" + str);
    }, "张无忌").start();

    new thread(() -> {
      string str = null;
      try {
        str = exchanger.exchange("假的倚天剑");
      } catch (interruptedexception e) {
        e.printstacktrace();
      }
      system.out.println("交易成功," + thread.currentthread().getname() + "获得" + str);
    }, "成昆").start();
  }
}

// 输出结果如下
交易成功,周芷若获得假的倚天剑
交易成功,成昆获得屠龙刀

当然,在等待交换信息的线程是可以被中断的,就比如玩家在等待交易过程中,突然玩家下线了,那就应该中断线程等待。

package com.chenpi;

import java.lang.thread.state;
import java.util.arraylist;
import java.util.list;
import java.util.concurrent.exchanger;

/**
 * @description
 * @author 陈皮
 * @date 2021/7/11
 * @version 1.0
 */
public class chenpimain {

  public static void main(string[] args) throws interruptedexception {

    exchanger<string> exchanger = new exchanger<>();

    list<thread> threads = new arraylist<>(3);

    thread thread1 = new thread(() -> {
      string str = null;
      try {
        str = exchanger.exchange("屠龙刀");
      } catch (interruptedexception e) {
        e.printstacktrace();
      }
      system.out.println("交易成功," + thread.currentthread().getname() + "获得" + str);
    }, "周芷若");
    threads.add(thread1);

    thread thread2 = new thread(() -> {
      string str = null;
      try {
        str = exchanger.exchange("倚天剑");
      } catch (interruptedexception e) {
        e.printstacktrace();
      }
      system.out.println("交易成功," + thread.currentthread().getname() + "获得" + str);
    }, "张无忌");
    threads.add(thread2);

    thread thread3 = new thread(() -> {
      string str = null;
      try {
        str = exchanger.exchange("假的屠龙刀");
      } catch (interruptedexception e) {
        e.printstacktrace();
      }
      system.out.println("交易成功," + thread.currentthread().getname() + "获得" + str);
    }, "成昆");
    threads.add(thread3);

    for (thread thread : threads) {
      thread.start();
    }

    // 等待5秒
    thread.sleep(5000);

    for (thread thread : threads) {
      system.out.println(thread.getname() + ":" + thread.getstate());
      // 如果还在阻塞等待则中断线程
      if (thread.getstate() == state.waiting) {
        thread.interrupt();
      }
    }
  }
}

// 输出结果如下
交易成功,张无忌获得屠龙刀
交易成功,周芷若获得倚天剑
周芷若:terminated
张无忌:terminated
成昆:waiting
交易成功,成昆获得null
java.lang.interruptedexception
	at java.util.concurrent.exchanger.exchange(exchanger.java:568)
	at com.chenpi.chenpimain.lambda$main$2(chenpimain.java:47)
	at java.lang.thread.run(thread.java:748)

上面演示的是线程如果等不到另一个线程和它交换信息,则会一直等待下去。其实 exchanger 还可以设置等待指定时间。比如系统设置玩家交换装备匹配时间为60秒,如果超出时间则终止交易。

package com.chenpi;

import java.util.concurrent.exchanger;
import java.util.concurrent.timeunit;
import java.util.concurrent.timeoutexception;

/**
 * @description
 * @author 陈皮
 * @date 2021/7/11
 * @version 1.0
 */
public class chenpimain {

  public static void main(string[] args) {

    exchanger<string> exchanger = new exchanger<>();

    new thread(() -> {
      try {
        // 超时时间设置为5秒
        string str = exchanger.exchange("屠龙刀", 5, timeunit.seconds);
        system.out.println("交易成功," + thread.currentthread().getname() + "获得" + str);
      } catch (timeoutexception e) {
        system.out.println("交易超时!");
        e.printstacktrace();
      } catch (interruptedexception e) {
        system.out.println("交易异常终止");
        e.printstacktrace();
      }
    }, "周芷若").start();
  }
}

// 输出结果如下
交易超时!
java.util.concurrent.timeoutexception
	at java.util.concurrent.exchanger.exchange(exchanger.java:626)
	at com.chenpi.chenpimain.lambda$main$0(chenpimain.java:22)
	at java.lang.thread.run(thread.java:748)

3 exchanger 应用

exchager 在遗传算法和管道设计等应用中是非常有用的。比如两个线程之间交换缓冲区,填充缓冲区的线程在需要时从另一个线程获得一个刚清空的缓冲区,并将填充的缓冲区传递给清空缓冲区的线程。

package com.chenpi;

import java.awt.image.databuffer;
import java.util.concurrent.exchanger;

/**
 * @description
 * @author 陈皮
 * @date 2021/7/11
 * @version 1.0
 */
public class chenpimain {

  exchanger<databuffer> exchanger = new exchanger<databuffer>();
  databuffer initialemptybuffer = ... a made-up type
  databuffer initialfullbuffer = ...

  class fillingloop implements runnable {

    public void run() {
      databuffer currentbuffer = initialemptybuffer;
      try {
        while (currentbuffer != null) {
          addtobuffer(currentbuffer);
          if (currentbuffer.isfull()) {
            currentbuffer = exchanger.exchange(currentbuffer);
          }
        }
      } catch (interruptedexception ex) { ...handle ...}
    }
  }

  class emptyingloop implements runnable {

    public void run() {
      databuffer currentbuffer = initialfullbuffer;
      try {
        while (currentbuffer != null) {
          takefrombuffer(currentbuffer);
          if (currentbuffer.isempty()) {
            currentbuffer = exchanger.exchange(currentbuffer);
          }
        }
      } catch (interruptedexception ex) { ...handle ...}
    }
  }

  void start() {
    new thread(new fillingloop()).start();
    new thread(new emptyingloop()).start();
  }
}

 

点击关注,第一时间了解华为云新鲜技术~

(0)
打赏 微信扫一扫 微信扫一扫

您想发表意见!!点此发布评论

推荐阅读

【高手问答汇总】游戏服务器十问,《百万在线》作者倾情作答

08-04

小游戏如何应对大流量?Shopee Shake 的大促实践

08-04

下载速率提升40% ,《斗罗大陆:魂师对决》是如何做到的?

08-04

华为帐号“一号畅玩”体验,助力游戏用户增长

08-04

WebGL着色器渲染小游戏实战

08-04

蜗牛游戏独立游戏分支Wandering Wizard获两款惊悚新游发行权

08-04

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论