多线程,性能和精度的考虑
multi-threading, performance and precision consideration
考虑以下 class:
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;
}
}
我可以采取哪些预防措施来确保这个 class 在多线程方面没有任何问题。有一个好的表现。同时确保金钱精度不会成为问题
使 POJO(普通旧 Java 对象)线程安全有几个关键点:
使 class 不可变。如果添加任何实例变量,请将它们设置为最终变量或可变变量。
使您的 getter 和 setter 同步,即
public synchronized void setAmount(double amount) {}
理想情况下,如果您将 class 设为不可变,您就不必为多线程而烦恼。但是在这里,您应该使 multiplyBy 方法可以相互执行,这样它就不会出现不一致的行为。
此外,您不需要提供 setter,因为唯一的构造函数将 amount 作为参数。
艾哈迈德,你的问题够模棱两可了。
关于多线程:不清楚你所说的多线程问题是什么意思。例如,class 在同步良好的意义上与多线程没有任何问题,但您仍然可以将 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;
}
}
关于金钱精度: 正如安德烈亚斯的回答,参见:Why not use Double or Float to represent currency?. Also that one may be interesting: What is the best data type to use for money in Java app?
保持精度
有几种保持精度的方法。第一个是 完全避免固定精度的浮点二进制数类型,例如 float
s 和 double
s 如果您的货币使用小数点之后的小数位。这里有一些不错的选择:
BigDecimal
java.math.BigDecimal
允许您轻松存储精确的有限长十进制值,但它可能有点慢。
如果您需要简单的编程和精确的结果,请使用 BigDecimal
s,但您可以接受缓慢。
long
如果您使用的是美元,long
可用于以美分而不是美元存储金额。
对于其他货币,您可以取货币面额的有理GCD的倒数,然后在存储时乘以它。
迷茫?这里是 an example of Wolfram|Alpha doing all the hard work of figuring out from the available US currency denominations (/100 through 0) that it should multiply US currency by 100。确保使用分数而不是小数。
如果您需要很高的速度并且可以接受货币金额大于 92,233,720,368,547,758.07 的缺点,则使用 long
s 会给出完全错误的结果。
除了自身速度快之外,long
s 还使用更少的内存并且从不需要对它们进行垃圾回收,因此这对它们来说是另一个小的加速。
BigInteger
long
s 可以替换为 java.math.BigInteger
s 以避免任何溢出问题。
如果您想要介于其他两者的速度和慢度之间并且没有合理的溢出机会,请使用此选项。
考虑以下 class:
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;
}
}
我可以采取哪些预防措施来确保这个 class 在多线程方面没有任何问题。有一个好的表现。同时确保金钱精度不会成为问题
使 POJO(普通旧 Java 对象)线程安全有几个关键点:
使 class 不可变。如果添加任何实例变量,请将它们设置为最终变量或可变变量。
使您的 getter 和 setter 同步,即
public synchronized void setAmount(double amount) {}
理想情况下,如果您将 class 设为不可变,您就不必为多线程而烦恼。但是在这里,您应该使 multiplyBy 方法可以相互执行,这样它就不会出现不一致的行为。 此外,您不需要提供 setter,因为唯一的构造函数将 amount 作为参数。
艾哈迈德,你的问题够模棱两可了。
关于多线程:不清楚你所说的多线程问题是什么意思。例如,class 在同步良好的意义上与多线程没有任何问题,但您仍然可以将 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;
}
}
关于金钱精度: 正如安德烈亚斯的回答,参见:Why not use Double or Float to represent currency?. Also that one may be interesting: What is the best data type to use for money in Java app?
保持精度
有几种保持精度的方法。第一个是 完全避免固定精度的浮点二进制数类型,例如 float
s 和 double
s 如果您的货币使用小数点之后的小数位。这里有一些不错的选择:
BigDecimal
java.math.BigDecimal
允许您轻松存储精确的有限长十进制值,但它可能有点慢。
如果您需要简单的编程和精确的结果,请使用 BigDecimal
s,但您可以接受缓慢。
long
如果您使用的是美元,long
可用于以美分而不是美元存储金额。
对于其他货币,您可以取货币面额的有理GCD的倒数,然后在存储时乘以它。
迷茫?这里是 an example of Wolfram|Alpha doing all the hard work of figuring out from the available US currency denominations (/100 through 0) that it should multiply US currency by 100。确保使用分数而不是小数。
如果您需要很高的速度并且可以接受货币金额大于 92,233,720,368,547,758.07 的缺点,则使用 long
s 会给出完全错误的结果。
除了自身速度快之外,long
s 还使用更少的内存并且从不需要对它们进行垃圾回收,因此这对它们来说是另一个小的加速。
BigInteger
long
s 可以替换为 java.math.BigInteger
s 以避免任何溢出问题。
如果您想要介于其他两者的速度和慢度之间并且没有合理的溢出机会,请使用此选项。