【问题标题】:Can a Thread in an ExecutorService call a method on a seperate thread?ExecutorService 中的线程可以调用单独线程上的方法吗?
【发布时间】:2017-12-19 00:58:03
【问题描述】:

我有一个套接字服务器,它使用 ExecutorService 为每个新套接字创建一个新线程。我还有一个类的静态实例,它可以进行所有线程都使用的数据库调用。

我的服务器用于在线国际象棋比赛。当用户进行移动时,移动被发送到服务器,并在数据库中创建一个条目,其中包含有关移动的一般信息(包括匹配的 ID)。每隔 10 秒左右,如果比赛的其他客户端也有一个到服务器的活动套接字,它将要求服务器获取有关比赛的所有新数据。

它可以工作,但正如您可以想象的那样,如果连接了非平凡数量的玩家,效率会非常低。我想要的是一种让线程窥视线程池并根据 ID(使用该线程的客户端的 ID)找到另一个线程的方法,然后调用该线程上的方法以向对方球员。

我一直在寻找,但我没有运气。这样的事情可能吗?如果是,是否可取?即使在代码方面有点冒险,我也愿意采取额外的措施来降低风险,从而获得巨大的资源节约优势。

【问题讨论】:

  • 方法属于类/对象而不是线程。您需要的是一种在 2 个玩家/线程之间发出信号的方式,表明已经采取了行动。想想当一个玩家按下按钮,另一个需要移动时的两个时钟
  • 播放器类中的两个原子/易失性布尔值应该可以为您解决问题
  • 让数据库不碍事。仅当您想稍后重新创建匹配时才有用。玩家只需要对手的最后一步,甚至布尔值可能都是额外的。
  • 是的,这篇文章让我很困惑,因为线程中不存在未调用的方法——恐怕这没有意义,而且你不能从一个线程在单独的线程上调用方法。这个结构根本不存在。
  • 是的。它会让你大吃一惊,直到你发现这是不可能的。

标签: java multithreading sockets server


【解决方案1】:

就像我在评论中所说,您的问题令人困惑;如果您只想在玩家移动时通知对手,最简单的实现是使用BlockingQueue。 Javadoc 甚至有代码示例,所以它应该很容易实现。在您的情况下,每当玩家移动时,您将一个项目放入队列中,消费者拿起并通知正在参与同一游戏的对手。您不需要搞乱低级线程构造,如果您甚至考虑根据池中的 id 来查找线程,那您就大错特错了。

BlockingQueue 可以工作,但它需要忙于等待,所以我不是它的忠实粉丝。相反,您可以使用观察者设计模式; JDK 已经对此提供了支持。下面是我自己编的一个例子:

public class Main extends Observable implements Observer {
    private final int numCores = Runtime.getRuntime().availableProcessors();
    private final ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(numCores);

    public Main() {
        addObserver(this);
    }

    public static void main(String[] args) throws InterruptedException {
        new Main().execute();
    }

    private void execute() {
        for (int i = 0; i < 5; ++i) {
            this.setChanged();
            this.notifyObservers(i);

            try {
                Thread.sleep(1000l);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        executor.shutdown();
    }

    @Override
    public void update(Observable o, Object arg) {
        System.out.printf("Received notification on thread: %s.\n", Thread.currentThread().getName());
        executor.submit(() -> System.out.printf("Running in thread: %s, result: %s.\n",
                Thread.currentThread().getName(), arg));
    }
}

Received notification on thread: main.
Running in thread: pool-1-thread-1, result: 0.
Received notification on thread: main.
Running in thread: pool-1-thread-2, result: 1.
Received notification on thread: main.
Running in thread: pool-1-thread-3, result: 2.
Received notification on thread: main.
Running in thread: pool-1-thread-4, result: 3.
Received notification on thread: main.
Running in thread: pool-1-thread-5, result: 4.

最后但并非最不重要的一点是,如果您真的想更上一层楼,请使用消息传递。您没有提到您是否使用了框架(同样,您缺乏信息),但 Spring 支持消息传递,Akka、Play 和 Camel 也是如此。

【讨论】:

    【解决方案2】:

    您可以创建提供您自己的 ThreadFactory 的 ExecutorService 来创建您自己的类,该类扩展 Thread 并具有对 ThreadFactory 本身的引用。 ThreadFactory 应该跟踪所有创建的 Thread 并能够通过它们的 ID 来识别它们。这样,每个 Thread 都可以向 ThreadFactory 查询某个 ID。

    【讨论】:

    • 您打算使用线程内的哪个对象来调用该方法?
    • ThreadFactory 本身,它知道所有线程及其状态,并且每个线程都有它的引用来询问其他线程,正如我所说的。你有什么从你的声誉的高度看到的吗?如果不是,你是不是因为短视误会了我的名声?先问,再决定。
    • 请阅读 cmets 并理解问题。您将线程状态与国际象棋比赛进度混淆了。线程类中的哪个方法为您提供了国际象棋比赛实例?
    • 如果你有播放器处理程序,为什么还需要其他东西?您不需要线程 ID。连队列都没有。在这种情况下,队列就像通勤的 18 轮车。两个玩家处理程序在一个具有最后移动位置、棋子和玩家的小类上同步。你最好删除答案。
    • 这里不讨论。抱歉,请阅读一些基本的线程书籍
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-01-21
    • 2015-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多