I use java classes of type Callable (up to 200 in parallel), which call a method of a tool class (centralizing common methods), and I notice that if this method is not 'synchronized', I get errors. I've tried moving this method to the Callable class, but the problem is still there.
With 'synchronized', it works, but I'm afraid that this setting will slow down performance, as tasks launched in parallel can't then be completely independent and will slow each other down, not running at their maximum speed.
What would be the best way to implement this?
Below, (simplified) extract from my code. Without 'synchronized', there seems to be no error as long as the data comparison doesn't deal with dates. But if the method compares dates (actually, more precisely, a mixture of java.util.Date and Timestamp, which I convert to date, then string), then I get errors. But the method works fine, because as soon as I switch to synchronized, no error...
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's API is semi-broken in that it guarantees the existence of
getTimestamp/setTimestamp, based onjava.sql.Timestamp, as well assetDate/getDate, based onjava.sql.Date. Both of those types extendjava.util.Datewhich is unfortunate; all these types are broken.JDBC as a design has decided that making a
getFoobar()method for every relevant data type is no longer a scalable proposition. The new JDBC way is to use the any-typed get and set methods:LocalDate and OffsetDateTime are all guaranteed to work. LocalDateTime and ZonedDateTime depends on your JDBC implementation. For example, Postgres JDBC driver suports
LocalDate,LocalTime,LocalDateTime, andOffsetDateTime. Which are the java equivalents for, respectively,DATE,TIME WITHOUT TIME ZONE,TIMESTAMP WITHOUT TIME ZONE, andTIMESTAMP WITH TIME ZONE. AndTIMEandTIMESTAMPare aliases for the without time zone variants.For PreparedStatements,
prepStatement.setObject(1, localDateInstance)is how its done (there is nosetLocalDatemethod, nor will there ever be - JDBC design has gone withsetObjectfor all future types relevant to DB interactions).Crucially all these classes, from the
java.timepackage, aren't broken, and match how DBs work, which is to store human reckoning terms directly. E.g. a DATE stores, in the database, 3 numbers: a year, a month, and a day.java.time.LocalTimeworks the same way.java.sql.Date(andjava.util.Date, the class it extends) do not which makes them very very evil classes because they are blatant lies. They store millis passed since the epoch and attempt to translate this into dates using some math which means timezone issues can mess this up. And just about every place on the planet has changed some aspect of time zones one way or another (such as shifting the date when winter time or summmer time kicks in), which is why it's so perniciously bad to rely on timezone info: It seems to work fine, until it doesn't. Bugs that are not easily testable are really bad bugs. Which is why it is so very very important you do not ever usejava.util.Dateand all its evil offspring, such asjava.sql.Dateandjava.sql.Timestamp.Once you have
java.timeobjects: They have.isEqualmethods, their formatting and parsing tooling is thread safe, and so on. Your problems will disappear.