我使用 Callable 类型的 java 类(最多并行 200 个),它调用工具类的方法(集中公共方法),我注意到如果这个方法不是“同步”的,我会收到错误。 我尝试过将此方法移至 Callable 类,但问题仍然存在。
使用“同步”,它可以工作,但我担心此设置会降低性能,因为并行启动的任务不能完全独立,并且会相互减慢,而不是以最大速度运行。
实现这一点的最佳方法是什么?
下面(简化)摘自我的代码。如果没有“同步”,只要数据比较不处理日期,似乎就不会出现错误。但是,如果该方法比较日期(实际上,更准确地说,是 java.util.Date 和 Timestamp 的混合,我将其转换为日期,然后转换为字符串),则会出现错误。但该方法工作正常,因为一旦我切换到同步,就没有错误......
public class CompareTableTF {
...
public void compareTable(...) {
...
while (...) {
try {
myTask = new CompareTableTask(...);
completion.submit(myTask);
}
catch (InterruptedException | ExecutionException e) {...}
...
}
}
public class CompareTableTask implements Callable<Integer> {
private List<Object> row1;
public CompareTableTask(List<Object> row1) {
this.row1=row1;
}
@Override
public Integer call() {
int result=1;
List<Object> row2=getRowToCompare(...);
if (isEqual(row2)) {
result=0;
}
return result;
}
private boolean isEqual(List<Object> row2) {
...
for (int i=0; i<n; i++) {
if (! Tools.areEqual(row1.get(i), row2.get(i))) return false;
}
return true;
}
public class Tools {
private final static SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S");
...
public synchronized static boolean areEqual(Object obj1, Object obj2) {
String sObj1, sObj2;
Double dObj1, dObj2;
Date dtObj1, dtObj2;
boolean resultat = true;
// Check if one only is empty
...
// Compare as objects
if (!obj1.equals(obj2)) {
// if differents, compare as strings
sObj1=obj1.toString();
sObj2=obj2.toString();
if (!sObj1.equals(sObj2)) {
// if differents, verify if are instanceof java.util.Date, convert them to strings, compare as strings
if (obj1 instanceof Date && obj2 instanceof Date) {
if (obj1 instanceof Timestamp) {dtObj1 = new Date(((Timestamp) obj1).getTime());} else {dtObj1 = (Date) obj1;}
if (obj2 instanceof Timestamp) {dtObj2 = new Date(((Timestamp) obj2).getTime());} else {dtObj2 = (Date) obj2;}
sObj1 = dateTimeFormat.format(dtObj1);
sObj2 = dateTimeFormat.format(dtObj2);
result = sObj1.equals(sObj2);
}
else {
// if differents (and not date), last compare as Double
resultat=false;
try {
dObj1 = new Double(sObj1);
dObj2 = new Double(sObj2);
if (dObj1.equals(dObj2)) {
resultat=true;
}
}
catch (NumberFormatException e) {
}
}
}
return result;
}
}
JDBC 的 API 是半破损的,因为它保证了基于
getTimestamp
的 setTimestamp
/java.sql.Timestamp
的存在,以及基于 setDate
的 getDate
/java.sql.Date
的存在。不幸的是,这两种类型都扩展了 java.util.Date
;所有这些类型都已损坏。
JDBC 作为一种设计,决定为每个相关数据类型创建
getFoobar()
方法不再是一个可扩展的主张。新的 JDBC 方式是使用任意类型的 get 和 set 方法:
try (ResultSet rs = ....) {
LocalDate ld = rs.getObject(1, LocalDate.Class); // right
java.sql.Date d = rs.getDate(1); // wrong
}
LocalDate 和 OffsetDateTime 都保证可以工作。 LocalDateTime 和 ZonedDateTime 取决于您的 JDBC 实现。例如,Postgres JDBC 驱动程序支持
LocalDate
、LocalTime
、LocalDateTime
和 OffsetDateTime
。分别是 DATE
、TIME WITHOUT TIME ZONE
、TIMESTAMP WITHOUT TIME ZONE
和 TIMESTAMP WITH TIME ZONE
的 Java 等效项。 TIME
和 TIMESTAMP
是 without time zone 变体的别名。
对于PreparedStatements,
prepStatement.setObject(1, localDateInstance)
是它的完成方式(没有setLocalDate
方法,也永远不会有——对于所有与数据库交互相关的未来类型,JDBC设计已经采用了setObject
)。
至关重要的是,来自
java.time
包的所有这些类都没有被破坏,并且与数据库的工作方式相匹配,即直接存储人类计算术语。例如。 DATE 在数据库中存储 3 个数字:年、月和日。 java.time.LocalTime
的工作原理相同。 java.sql.Date
(和java.util.Date
,它扩展的类)do not,这使得它们非常非常邪恶的类,因为它们是公然的谎言。他们存储自纪元以来经过的毫秒数,并尝试使用一些数学方法将其转换为日期,这意味着时区问题可能会搞砸。地球上几乎每个地方都以这样或那样的方式改变了时区的某些方面(例如在冬令时或夏令时开始时改变日期),这就是为什么依赖时区信息是如此有害的原因:看起来正常工作,直到不能正常工作为止。不容易测试的错误才是真正糟糕的错误。这就是为什么它非常非常重要,您永远不要使用java.util.Date
及其所有邪恶的后代,例如java.sql.Date
和java.sql.Timestamp
。
一旦有了
java.time
对象:它们就有 .isEqual
方法,它们的格式化和解析工具是线程安全的,等等。你的问题将会消失。