【问题标题】:multi-threading, performance and precision consideration多线程、性能和精度考虑
【发布时间】:2018-09-26 17:05:35
【问题描述】:

考虑以下类:

public class Money {
    private double amount;

    public Money(double amount) {
        super();
        this.amount = amount;
    }

    public double getAmount() {
        return amount;
    }

    public void setAmount(double amount) {
        this.amount = amount;
    }

    public Money multiplyBy( int factor) {
        this.amount *= factor;
        return this;
    }
}

我可以采取哪些预防措施来确保此类在多线程方面没有任何问题。有良好的表现。同时确保货币精度不会成为问题

【问题讨论】:

标签: java multithreading precision currency money-format


【解决方案1】:

艾哈迈德,你的问题太模棱两可了。

关于多线程:不清楚您所说的多线程问题是什么意思。例如,该类在同步良好的意义上对多线程没有任何问题,但是您仍然可以将 Money 对象的状态设置为混乱,由多个线程使用它:

public class Money {
    private volatile double amount;

    public Money(double amount) {
        super();
        this.amount = amount;
    }

    public double getAmount() {
        return amount;
    }

    public synchronized void setAmount(double amount) {
        this.amount = amount;
    }

    public synchronized Money multiplyBy( int factor) {
        this.amount *= factor;
        return this;
    }
}

关于货币精确度:正如 Andreas 所回答的,请参阅:Why not use Double or Float to represent currency?。还有一个可能很有趣:What is the best data type to use for money in Java app?

【讨论】:

    【解决方案2】:

    理想情况下,如果您将类设为不可变,则不必为多线程而烦恼。但是在这里,您应该将 multiplyBy 方法设置为相互可执行的,这样它就不会出现不一致的行为。 此外,您不需要提供 setter,因为唯一的构造函数将数量作为参数。

    【讨论】:

      【解决方案3】:

      有几个关键点可以使 POJO(普通旧 Java 对象)线程安全:

      1. 使类不可变。如果添加任何实例变量,请将它们设为 final 或 volatile。

      2. 让你的 getter 和 setter 同步,即

        public synchronized void setAmount(double amount) {}
        

      【讨论】:

        【解决方案4】:

        保持精度

        有几种方法可以保持精度。首先是完全避免使用固定精度浮点二进制数类型,如floats 和doubles,如果您的货币使用超过该点的十进制数字。以下是一些不错的选择:

        BigDecimal

        java.math.BigDecimal 允许您轻松存储精确的有限长十进制值,但它可能有点慢。

        如果您需要简单的编程和精确的结果,请使用BigDecimals,但您可以接受速度缓慢。

        long

        如果您使用美元,long 可用于以美分而非美元存储金额。

        对于其他货币,您可以取货币面额的有理 GCD 的倒数,然后在存储时乘以该值。

        困惑?这是an example of Wolfram|Alpha doing all the hard work of figuring out from the available US currency denominations ($1/100 through $100) that it should multiply US currency by 100。确保使用分数而不是小数。

        如果您需要很高的速度并且可以接受longs,但缺点是货币金额大于 92,233,720,368,547,758.07 美元会给出完全错误的结果。

        除了自身速度快之外,longs 还使用更少的内存,并且从不需要自己进行垃圾收集,所以这对他们来说是另一个小的加速。

        BigInteger

        longs 可以替换为java.math.BigIntegers 以避免任何溢出问题。

        如果您想要介于其他两者的速度和缓慢之间且没有合理机会溢出的情况,请使用此选项。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-03-07
          • 1970-01-01
          • 2016-07-18
          • 2019-07-26
          相关资源
          最近更新 更多