0

我正在开发一个应用程序,该应用程序具有必须对所有实例可用的对象,但还具有对对象内某些方法的同步访问权限。

例如我有这个对象:

public class PlanetID implements Serializable {
    public PlanetID() {
        id = 0;
    }

    public long generateID() {
        id++;
        return id;
    }

    private long id;
}

这是一个简单的对象,可以创建一个长的(id)系列。这个对象每次都必须生成一个唯一的 id。目前,我有一个静态同步方法,可以处理 Datastore 访问和存储以及 MemCache 访问和存储。它适用于这种特定方法,但我已经可以看到更复杂对象的问题,这些对象需要用户能够访问非同步变量以及同步变量。

是否有某种方法可以使对象成为全局对象,并在访问这些同步对象时允许同步方法和非同步方法以及对象的存储?

编辑:我认为人们过于关注我给他们的例子,而不是更大的问题,即拥有一个可以被所有实例访问的全局变量,并同步访问特定方法,同时允许异步访问其他方法。

这是一个更好的例子,希望它能让事情变得更清晰。

前任。

public class Market implements Serializable {
public Market() {
    mineral1 = new ArrayList<Listing>();
    mineral2 = new ArrayList<Listing>();
    mineral3 = new ArrayList<Listing>();
    mineral4 = new ArrayList<Listing>();
}

public void addListing(int mineral, String userID, int price, long amount) { //Doesn't require synchronized access
    switch (mineral) {
    case MINERAL1:
        mineral1.add(new Listing(userID, price, amount));
        break;
    case MINERAL2:
        mineral2.add(new Listing(userID, price, amount));
        break;
    case MINERAL3:
        mineral3.add(new Listing(userID, price, amount));
        break;
    case MINERAL4:
        mineral4.add(new Listing(userID, price, amount));
        break;
    }
}

public void purchased(int mineral, String userID, long amount) { //Requires synchronized access
    ArrayList<Listing> mineralList = null;

    switch (mineral) {
    case MINERAL1:
        mineralList = mineral1;
        break;
    case MINERAL2:
        mineralList = mineral2;
        break;
    case MINERAL3:
        mineralList = mineral3;
        break;
    case MINERAL4:
        mineralList = mineral4;
        break;
    }       

    Listing remove = null;
    for (Listing listing : mineralList)
        if (listing.userID == userID)
            if (listing.amount > amount) {
                listing.amount -= amount;
                return;
            } else{
                remove = listing;
                break;
            }

    mineralList.remove(remove);
            Collections.sort(mineralList);
}

public JSONObject toJSON(int mineral) { //Does not require synchronized access
    JSONObject jsonObject = new JSONObject();

    try {
        switch (mineral) {
        case MINERAL1:
            for (Listing listing : mineral1)
                jsonObject.accumulate(Player.MINERAL1, listing.toJSON());
            break;
        case MINERAL2:
            for (Listing listing : mineral2)
                jsonObject.accumulate(Player.MINERAL2, listing.toJSON());
            break;
        case MINERAL3:
            for (Listing listing : mineral3)
                jsonObject.accumulate(Player.MINERAL3, listing.toJSON());
            break;
        case MINERAL4:
            for (Listing listing : mineral4)
                jsonObject.accumulate(Player.MINERAL4, listing.toJSON());
            break;
        }
    } catch (JSONException e) {

    }

    return jsonObject;
}

public static final int MINERAL1 = 0;
public static final int MINERAL2 = 1;
public static final int MINERAL3 = 2;
public static final int MINERAL4 = 3;

private ArrayList<Listing> mineral1;
private ArrayList<Listing> mineral2;
private ArrayList<Listing> mineral3;
private ArrayList<Listing> mineral4;

private class Listing implements Serializable, Comparable<Listing> {
    public Listing(String userID, int price, long amount) {
        this.userID = userID;
        this.price = price;
        this.amount = amount;
    }

    public JSONObject toJSON() {
        JSONObject jsonObject = new JSONObject();

        try {
            jsonObject.put("UserID", userID);
            jsonObject.put("Price", price);
            jsonObject.put("Amount", amount);
        } catch (JSONException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return jsonObject;
    }

    @Override
    public int compareTo(Listing listing) {
        return (price < listing.price ? -1 : (price == listing.price ? 0 : 1));
    }

    public String userID;
    public int price;
    public long amount;
}

}

4

5 回答 5

1

使用 GAE,Java 语言不会为您隐藏所有数据存储抽象。

停止思考全局变量和方法。这些是 Java 语言结构。开始考虑数据存储结构 - 实体、数据存储访问和事务。

在 GAE 上,您的代码将同时在许多服务器上运行,它们不会共享全局变量,“共享数据”在数据存储(或内存缓存)中

实体是数据存储中的对象。您可以从代码中的任何位置获取数据存储,因此它们可以替换您的全局变量。您在方法中定义事务以同步数据存储访问并确保事务只发生一次。有些方法可以使用事务,不需要同步的时候不要使用事务。

你不应该需要你的全球矿物数组列表。当您处理购买时,您基本上需要从数据存储中获取列表的事务,或者如果它不存在则创建它,更新用户并将其写回数据存储。在继续之前,您可能需要阅读数据存储。

于 2012-04-12T14:42:21.017 回答
0

除了事务之外的其他方法是使用单个后端实例来保存您的全局对象,并让对对象的所有访问都在那里同步。所有其他实例都需要使用 URLFetch 访问此后端实例以获取对象的状态。

这是一个可怕的性能瓶颈,但如果您的应用程序想要顺利扩展,请不要使用它,我只是指出替代方法。事实上,如果可能的话,请首先避免在分布式应用程序上需要同步的全局对象。

于 2012-04-12T06:56:50.287 回答
0

虽然技术上可行,但您应该听取其他答案的建议并使用 Google 提供的服务,例如数据存储和内存缓存。

但是,您可以使用包含您的数据的单个后端,然后使用您喜欢的 RPC 方法将数据读取和写入共享对象。您需要注意,虽然它不经常发生,但不能保证后端不会随机死机 - 因此您可能会丢失此对象中的所有数据。

于 2012-04-12T15:12:20.167 回答
0

如评论中所述 - 您可以使用事务来实现此目的:

  1. 开始交易
  2. 使用和属性创建或更新SomeObject实体。indexsomeText
  3. 提交事务。如果两个实例同时尝试执行此操作,那么其中一个实例会出现异常并需要重试(再次获取数据、递增、全部放入事务中)。

编辑:

(删除了分片计数器的部分,因为它们不保证)

请注意,上述解决方案的写入瓶颈约为 1 写入/秒。如果您需要更高性能的解决方案,您可以考虑使用后端实例。

于 2012-04-12T06:49:26.880 回答
0

看看 DatastoreService.allocateIds - 无论您是否实际使用生成的 ID 来编写数据存储实体,都可以有效地从中获得唯一的长数字。

但是请注意,不能保证它们是按顺序排列的,只能保证它们是唯一的——问题并没有说明按顺序排列是必需的。

public class PlanetID implements Serializable
{
    private DatastoreService ds;

    public PlanetID()
    {
        ds = DatastoreServiceFactory.getDatastoreService();
    }

    public long generateID()
    {
        return ds.allocateIds("Kind_That_Will_Never_Be_Used", 1L).getStart().getId();
    }
}
于 2012-04-12T11:09:07.087 回答