concurrence – [Blog] du Java et du Freelance https://java-freelance.fr Vis ma vie de freelance java : techno et entreprise Mon, 12 Oct 2015 14:45:01 +0000 fr-FR hourly 1 https://wordpress.org/?v=4.4.4 Revue de presse https://java-freelance.fr/web/revue-de-presse-2 https://java-freelance.fr/web/revue-de-presse-2#respond Sat, 27 Mar 2010 22:29:33 +0000 https://java-freelance.fr/?p=480 Voici une liste des liens qui nous ont intéressés récemment :

Le domain driven design « Vite fait » : 80 pages pour changer votre vie. Ou plutôt changer votre façon d’écrire vos modèles métiers.

Cours sur la concurrence : Une piqure de rappel ne fait pas (de) mal.

Développer avec Comet et Java : Vous rêver d’envoyer des informations au navigateur en mode « push » ? Voici la solution.

Algodeal: Pour tous ceux qui aime inventer des algos, voici une occasion de faire fortune en s’amusant (et en java)  !

Les castcodeurs : Episode spécial « Freelance » avec Mathilde ! A écouter absolument si vous souhaitez vous mettre indépendant !

]]>
https://java-freelance.fr/web/revue-de-presse-2/feed 0
Problème de concurrence avec SimpleDateFormat https://java-freelance.fr/java/concurrence-simpledateformat https://java-freelance.fr/java/concurrence-simpledateformat#comments Tue, 16 Mar 2010 09:43:24 +0000 https://java-freelance.fr/?p=455 La classe DateUtil utilisée dans mon projet possède de nombreuse méthodes, certaines permettent de créer des identifiants basés sur les dates ensuite insérés en base de données. L’objet simpleDateFormat qui permet le parsage de ces dates est définit comme étant « public static final ».

Dans la plupart des cas, pas de problème, la date est bien parsée et l’identifiant est crée conformément à ce que l’on attends. Néanmoins, dans de rares cas, il est possible que cela se passe beaucoup moins bien. En effet, la classe SimpleDateFormat n’est pas synchronisée. Si 2 threads essaient en même temps d’utiliser cette instance pour un parsage ou un formatage, le résultat est aléatoire.

La classe DateUtil a été simplifiée à l’extrême.

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateUtil {

	public static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
			"ddMMyyyy");

	public static final Date parse(String date) throws ParseException{
		return simpleDateFormat.parse(date);
	}

	public static final String format(Date date) throws ParseException{
		return simpleDateFormat.format(date);
	}
}

La classe de test ci dessous fait en sorte que deux threads concurrents effectuent des parsages/formatages via les deux méthodes définies dans la classe DateUtil sur les trois dates de référence définies dans le tableau de String. Elle effectue ensuite une comparaison entre la date de référence et celle qui a été parsée+formatée.

import java.text.ParseException;

public class TestSimpleDateFormat {

  public static void main(String[] args) {
    final String[] tabDateString = new String[] {
        "01012001", "08082008", "05052005" };

    Runnable runnable = new Runnable() {
      public void run() {
        try {
          for (int j = 0; j < 1000; j++) {
            for (int i = 0; i < 2; i++) {
              String date = DateUtil
                  .format(DateUtil
                      .parse(tabDateString[i]));
              if (!(tabDateString[i].equals(date))) {
                throw new ParseException(
                    tabDateString[i] + " =>"
                        + date, 0);
              }
            }
          }
        } catch (ParseException e) {
          e.printStackTrace();
        }

      }
    };
    new Thread(runnable).start();
    Runnable runnable2 = new Runnable() {
      public void run() {
        try {
          for (int j = 0; j < 1000; j++) {
            for (int i = 0; i < 2; i++) {
              String date = DateUtil
                  .format(DateUtil
                      .parse(tabDateString[i]));
              if (!(tabDateString[i].equals(date))) {
                throw new ParseException(
                    tabDateString[i] + " =>"
                        + date, 0);
              }
            }
          }
        } catch (ParseException e) {
          e.printStackTrace();
        }
      }
    };
    new Thread(runnable2).start();
  }

}

Les résultats sont plus que divers, voilà ci dessous la liste des exceptions les plus fréquentes.

java.text.ParseException: 08082008 =>08012008
	at TestSimpleDateFormat$1.run(TestSimpleDateFormat.java:19)
	at java.lang.Thread.run(Thread.java:619)
java.text.ParseException: 01012001 =>08012001
	at TestSimpleDateFormat$2.run(TestSimpleDateFormat.java:42)
	at java.lang.Thread.run(Thread.java:619)
Exception in thread "Thread-1" java.lang.NumberFormatException: For input string: ".808E.8082E2"
	at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1224)
	at java.lang.Double.parseDouble(Double.java:510)
	at java.text.DigitList.getDouble(DigitList.java:151)
	at java.text.DecimalFormat.parse(DecimalFormat.java:1303)
	at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1934)
	at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1312)
	at java.text.DateFormat.parse(DateFormat.java:335)
	at TestSimpleDateFormat$2.run(TestSimpleDateFormat.java:40)
	at java.lang.Thread.run(Thread.java:619)
Exception in thread "Thread-1" java.lang.NumberFormatException: multiple points
	at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1084)
	at java.lang.Double.parseDouble(Double.java:510)
	at java.text.DigitList.getDouble(DigitList.java:151)
	at java.text.DecimalFormat.parse(DecimalFormat.java:1303)
	at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1934)
	at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1312)
	at java.text.DateFormat.parse(DateFormat.java:335)
	at TestSimpleDateFormat$2.run(TestSimpleDateFormat.java:40)
	at java.lang.Thread.run(Thread.java:619)
Exception in thread "Thread-1" java.lang.NumberFormatException: For input string: ""
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
	at java.lang.Long.parseLong(Long.java:431)
	at java.lang.Long.parseLong(Long.java:468)
	at java.text.DigitList.getLong(DigitList.java:177)
	at java.text.DecimalFormat.parse(DecimalFormat.java:1298)
	at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1934)
	at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1312)
	at java.text.DateFormat.parse(DateFormat.java:335)
	at TestSimpleDateFormat$2.run(TestSimpleDateFormat.java:40)
	at java.lang.Thread.run(Thread.java:619)

Bien sur, ces erreurs n’arrivent en réalité qu’exceptionnellement. Parfois, des exceptions sont remontées, parfois cela a l’air de bien se passer mais l’objet crée est en fait une combinaison d’autres dates et ne correspond donc pas à la date attendue. Il y a plusieurs manière de rendre la classe DateUtil thread safe.

Le plus simple, ne pas rendre l’objet simpleDateFormat comme étant un attribut de la classe mais de le recréer à chaque appel.
Ex :

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateUtil {

	public static final Date parse(String date) throws ParseException{
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
		"ddMMyyyy");
		return simpleDateFormat.parse(date);
	}

	public static final String format(Date date) throws ParseException{
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
		"ddMMyyyy");
                return simpleDateFormat.format(date);
	}
}

Dans le cas de méthodes communes, il est possible que cela deviennent couteux.

Une deuxième méthode est la définition des deux méthodes parse & format comme étant synchronized :

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateUtil {

	public static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
			"ddMMyyyy");

	public synchronized static final Date parse(String date) throws ParseException{
		return simpleDateFormat.parse(date);
	}

	public synchronized  static final String format(Date date) throws ParseException{
		return simpleDateFormat.format(date);
	}
}

Cette méthode marche, néanmoins, il est possible qu’elle crée un goulet d’étranglement du au fonctionnement de synchronized, les méthodes format & parse pouvant être extrêmement communes dans une application. Néanmoins, elle offre l’avantage d’être simple et claire.

Une approche un peu plus fine est l’utilisation d’un objet ThreadLocal. Cet objet permet d’utiliser des variables comme étant locale à un thread. Tous les threads ont une copie de la variable.

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateUtil {

	private static ThreadLocal format = new ThreadLocal() {
        protected synchronized SimpleDateFormat initialValue() {
            return new SimpleDateFormat(
			"ddMMyyyy");
        }
    };

    private static SimpleDateFormat getSimpleDateFormat(){
    	return format.get();
    }

	public  static final Date parse(String date) throws ParseException{
		return getSimpleDateFormat().parse(date);
	}

	public  static final String format(Date date) throws ParseException{
		return getSimpleDateFormat().format(date);
		}
}

Comme je suis dans le cas où ma classe DateUtil n’a besoin que d’une méthode format, j’ai choisi une 4ème méthode, l’utilisation non plus de l’objet java.text.SimpleDateFormat mais org.apache.commons.lang.time.FastDateFormat . Le formatage est supportée, avec le même pattern que pour la classe SimpleDateFormat à l’exception du z (se référer à la doc).

]]>
https://java-freelance.fr/java/concurrence-simpledateformat/feed 8