【问题标题】:Two random walkers on a 2d plane二维平面上的两个随机步行者
【发布时间】:2015-06-21 20:42:32
【问题描述】:

所以我有这个生成 2 个随机步行者的多线程程序,每个步行者都是一个单独的线程,因为我需要它们同时移动。每个步行者随机向 4 个方向中的任何一个移动。第一个问题是我认为 stdDraw 不是线程安全的,因此在没有锁定我的整个函数的情况下,它往往会无缘无故地在随机点绘制随机方块,整个事情变得非常有问题。当我在我的函数周围加锁时,一个线程变得比另一个线程慢,因为它有时必须等待锁。所以这些威胁不再是同时发生的。有针对这个的解决方法吗?我遇到的另一个问题是我希望它在两个步行者相交时脱离循环,但由于某种原因,两个线程不知道另一个线程的位置。一个人认为另一个人的位置总是在(0,0)。谢谢!

import java.awt.Color;
public class WalkerThread implements Runnable {

String name;
static Integer lock = new Integer(1000);
int num;
static int steps = 0, steps2 = 0;
static int x = 0, y = 0;
static int x2 = -1, y2 = -2;

public WalkerThread(String s, int n) {
    this.name = s;
    this.num = n;
}

@Override
public void run() {

    int N = 10;
    StdDraw.create(600, 600);
    StdDraw.setScale(-N, -N, +N, +N);
    StdDraw.clear(Color.gray);
    do {
        synchronized (lock) {

            if (num == 1) {
                StdDraw.go(x, y);
                StdDraw.setColor(Color.white);
                StdDraw.spot(0.9, 0.9);

                double r = Math.random();
                if (r < 0.25)
                    x--;
                else if (r < 0.50)
                    x++;
                else if (r < 0.75)
                    y--;
                else if (r < 1.00)
                    y++;

                steps++;

                StdDraw.setColor(Color.blue);
                StdDraw.go(x, y);
                StdDraw.spot(0.9, 0.9);
                StdDraw.pause(40);

            }

            if (num == 2) {
                StdDraw.go(x2, y2);
                StdDraw.setColor(Color.yellow);
                StdDraw.spot(0.9, 0.9);
                double r2 = Math.random();
                if (r2 < 0.25)
                    x2--;
                else if (r2 < 0.50)
                    x2++;
                else if (r2 < 0.75)
                    y2--;
                else if (r2 < 1.00)
                    y2++;

                steps2++;
                StdDraw.setColor(Color.green);
                StdDraw.go(x2, y2);
                StdDraw.spot(0.9, 0.9);
                StdDraw.pause(40);

            }
        }// lock
        /*String pict = steps + ".png";
        StdDraw.save(pict);*/
            //if (posX == posX2 && posY == posY2) break;


    } while ((Math.abs(x) < N && Math.abs(y) < N) && (Math.abs(x2) < N && Math.abs(y2) < N));

    System.out.printf("Total steps of %s is %d and %d \n", name, steps, steps2);

}
}

//主要

public class Walkers{
public static void main(String[] args) {

    Thread t1 = new Thread(new WalkerThread("one", 1));
    Thread t2 = new Thread(new WalkerThread("two", 2));

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

}
}

【问题讨论】:

  • 您的 StdDraw 类属于哪个库?还有,为什么要在当前的 WalkerThread 实例上同步run() 方法呢?这有什么用?
  • 如果你能提供整个代码会更容易。您的代码肯定需要更改。为了更好更准确地提供帮助,是否可以提供所有代码?
  • 包括了我的 main (Walkers.java),以及前面评论中提到的两个库。就这样。让我知道你是否需要其他任何东西。

标签: java multithreading random thread-safety thread-synchronization


【解决方案1】:

在使用多线程时避免使用Math.random() - 在您的 Walker 构造函数中创建一个r = new Random(),并将其用作r.nextDouble()。

而不是大的if,取两个分支之间的差异(只是几个颜色)并将它们放在构造函数中。此外,线程具有单独的名称空间。您不需要将x 和x2 分开-每个线程都有自己的私有x,对另一个线程不可见。您的代码最终可能大约是原来的 1/2。

就同步而言,您有两个问题。第一个问题是StdDraw 是built on Swing(例如,它在JFrame 中运行),这不是线程安全的。特别是,所有绘制都必须发生在称为事件线程的东西中。这意味着您应该将所有绘图代码放在类似

SwingUtilities.invokeLater(new Runnable() {
  @Override
  public void run() {
    synchronized (lock) {
      // ... your calls to StdDraw here ...
    }
  }
});

但是,这会打开一大堆蠕虫。首先,绘图代码需要访问您的数据,因此您需要防止同时更改这些数据。你可以用更多的synchronized (lock) { ... } 来保护它,但这意味着在任何给定时刻只有一个线程会执行。这不是多线程的用途。

更简单的答案是,看看 Elyasin 的答案,忘记并行执行(这里真的不需要),并拥抱轮流:

do {
        bool turn = false;
        // ... current init code here
        if (turn) {
            // ... current code for num==1
        } else {
            // ... current code for num==2
        }
        turn = !turn; // reverse turn for next round
} while (/* ... */);

没有线程、没有锁、没有同步,它应该可以流畅地工作并且没有工件。

【讨论】:

  • 迄今为止最有趣的答案。谢谢你的主意。我现在学到了一些关于 Swing 的知识(y)。
【解决方案2】:

所以我有这个生成 2 个随机游走者的多线程程序, 每个walker都是一个单独的线程,因为我需要它们移动 同时。每个步行者随机向 4 个方向中的任何一个移动。

您明确声明您想要两个随机步行者,四个方向中的任何一个由两个步行者中的任何一个随机选择。所以我们坚持这个要求。

第一个问题是我认为stdDraw 不是线程安全的并且 因此如果没有锁定我的整个功能,它往往会 无缘无故地在随机点上绘制随机方块和整个事情 变得非常有问题。当我锁定我的功能时 线程变得比另一个慢,因为它有时必须等待 为锁。所以线程不再是同时的。有没有 解决办法?

线程安全性和随机性在这里并不真正相关。如上所述,您希望步行者是随机的。这首先与线程安全无关。简单地说:线程安全意味着如果多个线程共享一个数据结构/地址空间,那么对它的访问就可以保证不受竞争条件的影响。 不确定您对 无缘无故随机点上的随机方块是什么意思。锁通常用于授予执行权限,或授予对一个或多个共享资源的访问权限。不知道你为什么在这里使用锁,我看不到共享资源,我看不出你为什么使用锁来控制线程执行一次如果你不想要这个首先。

两个随机游走者是独立的,我看到的唯一共享资源是 2D 平面。 如果您希望两个步行者同时/同时执行,那么您不应该像我想的那样使用锁。 我什至不确定线程​​安全是否真的是一个问题,也许你不需要线程安全?

我遇到的另一个问题是我希望它在以下情况下跳出循环 两个步行者相交,但由于某种原因,这两个线程不 了解彼此的立场。一个人认为的立场 另一个总是在 (0,0)。

哦,现在这是一个很好的后续问题。也许有一个共享资源呢?那么它必须是线程安全的吗?

那是二维平面,它会知道两个步行者是否相交? (老实说,我没有查看StdDraw,但我认为你会知道。)找到一种方法从StdDraw 获取两个随机步行者的两个坐标并检查交叉点。如果这不可能,则使用共享资源,即同时保存第一个随机游走者和第二个随机游走者坐标的数据结构。

您不需要太关心线程安全,因为一个随机游走者只会读取(而不是写入)另一个随机游走者的值/坐标。

尝试一下并告诉我们。

【讨论】:

  • 你是对的 - 唯一的共享资源是 2D 平面。正如我已经提到的,如果我不使用锁,一个线程往往会在其他步行者所在的地方绘制。例如,如果一个步行者的头是蓝色的,尾巴是白色的,而另一个步行者的头是绿色的,尾巴是黄色的,那么第二个步行者的头和尾巴将随机地是白色和/或蓝色。反之亦然。当锁存在时,不会发生此故障。
  • 由于 StdDraw 是基于 Swing 的,所以无论同步多少,任何多线程绘制都会造成麻烦。
  • @Elyasin - 读取正在更新的内容仍可能导致结果不一致。
  • 只有两个整数。但是,是的,你是对的。它不是原子的,因此可能容易出错。
  • 好的,所以我在不使用线程的情况下重新编写了代码。所以一个步行者移动,然后另一个移动,然后更新。唯一的问题是,如果一个步行者撞到墙壁,他们都会停下来。如果其中一个碰撞另一个继续行走,则与线程一样。我猜工作正常。谢谢大家。
猜你喜欢
  • 2022-01-25
  • 1970-01-01
  • 2013-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多