Isso me lembrou uma coisa, @Entanglement.
Alguns livros recomendam fazer assim:
public static String formatarHora() {
synchronized (formatoHora) { .... }
}
Até parece uma boa idéia, entretanto, descobri da pior maneira possível que essa não é uma boa prática.
Isso porque você tem que ter absoluta certeza que outras classes não farão a mesma coisa que você, em especial, a própria classe DateFormat. Como você não pode ler o conteúdo interno das classes (pois a OO insiste nessa história de encapsulamento), pode acabar com um deadlock em mãos, afinal, você não sabe em que contextos outras classes podem ter a mesma idéia que você.
O ideal MESMO seria criar sua própria lock.
Agora, tudo isso poderia ser evitado se tudo isso não rodasse num contexto estático, mas de um objeto. Nesse contexto, você pode simplesmente considerar a possibilidade de cada thread rodar com seu próprio objeto, sem ter que para isso recorrer a threadlocals e outras coisas sinistras do tipo.
[quote=jmmenezes]
Como sempre evitei métodos estáticos, fiquei na duvida se este código pode apresentar problema:
public static String converterDataString(Date cal, String formato) throws CoreException {
try {
SimpleDateFormat sdf = new SimpleDateFormat(formato);
String dataStr = sdf.format(cal);
return dataStr;
} catch (Exception ex) {
throw new CoreException("Erro em converterDataString.", ex);
}
}
[/quote]
Este é o código correto. Sem sincronismo ou threadlocal. Ok que temos que criar um objeto a cada execução, mas e daí ? como eu disse isso é grátis na jvm e aliás é o que fazemos quando usamos o operador + com strings, toda a hora um StringBulder será criado… Criar objetos não é problema.
Este erro especifico do uso do SimpleDateFormat realmente é chato e é preciso ter lido o javadoc com muita atenção ou ter passado problemas com isso. Eu nem sabia deste problema (porque uso o codigo como acima), mas um dia resolvi que poderia tornar o formatador estático em uma classe de utils da vida. Como eu descobrir que era furada? usando o firebugs. Esta ferramenta é muito boa para avisar de coisas que não são nada intuitivas. Portanto, aconselho a passar o firebug nos vossos codigos a fim de descobrir furadas como esta ( ou a usar o sonar que já inclui o firebugs e muitas outras ferramentas de qualidade)
[quote=sergiotaborda][quote=jmmenezes]
Como sempre evitei métodos estáticos, fiquei na duvida se este código pode apresentar problema:
public static String converterDataString(Date cal, String formato) throws CoreException {
try {
SimpleDateFormat sdf = new SimpleDateFormat(formato);
String dataStr = sdf.format(cal);
return dataStr;
} catch (Exception ex) {
throw new CoreException("Erro em converterDataString.", ex);
}
}
[/quote]
Este é o código correto. Sem sincronismo ou threadlocal. Ok que temos que criar um objeto a cada execução, mas e daí ? como eu disse isso é grátis na jvm e aliás é o que fazemos quando usamos o operador + com strings, toda a hora um StringBulder será criado… Criar objetos não é problema.
Este erro especifico do uso do SimpleDateFormat realmente é chato e é preciso ter lido o javadoc com muita atenção ou ter passado problemas com isso. Eu nem sabia deste problema (porque uso o codigo como acima), mas um dia resolvi que poderia tornar o formatador estático em uma classe de utils da vida. Como eu descobrir que era furada? usando o firebugs. Esta ferramenta é muito boa para avisar de coisas que não são nada intuitivas. Portanto, aconselho a passar o firebug nos vossos codigos a fim de descobrir furadas como esta ( ou a usar o sonar que já inclui o firebugs e muitas outras ferramentas de qualidade)[/quote]
Foi o que falei a respeito do escopo (pelo menos aprendi assim) … rs rs rs
Mas achei que pudesse ter algo dentro do SimpleDateFormat que mesmo instânciando sempre o objeto pudesse dar problemas… por isso a duvida…
Estas ferramentas de validar código realmente ajudam a evitar alguns anti-patterns… só não acho correto seguir a risca tudo que elas recomendam (sempre há exceções onde sabemos o que estamos fazendo), mas eu também costumo usá-las bastante atualmente (um dia já cheguei a achar que elas só serviam para encher o saco, como muita gente pensa, mas isso muda :P)
[quote=sergiotaborda]
public static String converterDataString(Date cal, String formato) throws CoreException {
try {
SimpleDateFormat sdf = new SimpleDateFormat(formato);
String dataStr = sdf.format(cal);
return dataStr;
} catch (Exception ex) {
throw new CoreException("Erro em converterDataString.", ex);
}
}
[/quote]
Este é o código correto. Sem sincronismo ou threadlocal. Ok que temos que criar um objeto a cada execução, mas e daí ? como eu disse isso é grátis na jvm[/quote]
Grátis não é, o construtor de SimpleDateFormat não roda em tempo zero Ele precisa de fazer um parse da string de formatação, e esse parse não é tao trivial assim. Alocar o objeto pode ser quase de graça, mas criá-lo pode ser custoso.
[quote=entanglement][quote=sergiotaborda]
public static String converterDataString(Date cal, String formato) throws CoreException {
try {
SimpleDateFormat sdf = new SimpleDateFormat(formato);
String dataStr = sdf.format(cal);
return dataStr;
} catch (Exception ex) {
throw new CoreException("Erro em converterDataString.", ex);
}
}
[/quote]
Este é o código correto. Sem sincronismo ou threadlocal. Ok que temos que criar um objeto a cada execução, mas e daí ? como eu disse isso é grátis na jvm[/quote]
Grátis não é, o construtor de SimpleDateFormat não roda em tempo zero Ele precisa de fazer um parse da string de formatação, e esse parse não é tao trivial assim. Alocar o objeto pode ser quase de graça, mas criá-lo pode ser custoso.
[/quote]
Acho que ai já vamos entrar em um cenário onde esse custo terá ou não impacto nos processos do sistema, que poderão levar a adotar uma solução totalmente diferente!
Isso aconteceria se o objeto acessasse uma variável estática (não é o caso aqui).
Isso é chamado de classe “Thread-hostile”.
Ou seja, uma classe extremamente difícil de sincronizar. Veja que é algo extremamente sujeito a erros, já que ela não demonstraria ser hostil em sua interface pública (você só descobriria isso com muita analise, profiling e depuração… isso se descobrisse).
[quote=ViniGodoy][quote]
Foi o que falei a respeito do escopo (pelo menos aprendi assim) … rs rs rs
Mas achei que pudesse ter algo dentro do SimpleDateFormat que mesmo instânciando sempre o objeto pudesse dar problemas… por isso a duvida…
[/quote]
Isso aconteceria se o objeto acessasse uma variável estática (não é o caso aqui).
Isso é chamado de classe “Thread-hostile”.
Ou seja, uma classe extremamente difícil de sincronizar. Veja que é algo extremamente sujeito a erros, já que ela não demonstraria ser hostil em sua interface pública (você só descobriria isso com muita analise, profiling e depuração… isso se descobrisse).
[/quote]
E Vini? Você conhece algum da api do java que tenha este problema e precisamos tomar este cuidado ??? ou se tiver isso pode ser considerado um bug ???
Não conheço. E se tiver é bug.
[quote=entanglement][quote=ViniGodoy]Métodos estáticos são mais difíceis de usar em contextos de multiplas threads - o que pode ter um impacto significativamente ruim na performance;
[/quote]
Uma vez eu resolvi criar um método estático para formatar datas de acordo com um determinado padrão fixo de um SimpleDateFormat. O único problema é que o SimpleDateFormat não é thread-safe. (Eu não tinha lido as letras miúdas). Após bater a cabeça depois de alguns erros esporádicos muito estranhos, eu acabei descobrindo que:
a) Isso tinha sido registrado no Bug DataBase da Sun
b) e que a Sun (hoje Oracle) não ia resolver isso.
O método continuou estático, mas tive de usar um ThreadLocal para criar uma instância do SimpleDateFormat para cada thread. Isso foi muito chato de descobrir, e é um exemplo de que tipos de problemas podem ter métodos estáticos. [/quote]
mas o problema não foi o metodo estático, e sim a instancia estatica.
[quote=entanglement]Resolver resolve, mas eu não gosto de sincronização porque sincronização = serialização, e se você serializar uma operação lenta (formatação de datas) ela pode se tornar um gargalo no sistema.
Dentro da medida do possível, deve-se evitar a sincronização, a menos que isso seja inevitável e você não tenha realmente uma boa alternativa (no meu caso, uma ThreadLocal resolvia bem meu problema, porque transformava o problema de sincronizar uma operação lenta (formatar uma data) no problema de sincronizar uma operação que em média é rápida (escolher no HashMap embutido no ThreadLocal o objeto correspondente à thread corrente).
[/quote]
no java tem algo similar a threadvar?
Isso só vale se quando o método estático usa uma variável compartilhada (leia-se, externa ao método), não é Vini?[/quote]
+1
[quote=entanglement]Hum… lembrei de mais um detalhinho chato de static + synchronized.
Digamos que você, como eu, tenha o costume de organizar seus métodos estáticos utilitários em uma única classe. Se você tivesse feito algo como
public class Util {
protected Util() { }
public synchronized static String formatarData () { ... }
private static DateFormat formatoData = ...;
public synchronized static String formatarHora () { ... }
private static DateFormat formatoHora = ...;
}
vai constatar que a execução de formatarData() e a execução de formatarHora() estão sendo mutuamente excludentes, o que não é o efeito desejado porque elas deveriam ser independentes entre si (afinal de contas, synchronized deve proteger apenas o objeto que não é thread-safe, ou seja, formatoData ou formatoHora. É que
public synchronized static String formatarData() { ... }
public synchronized static String formatarHora () { ... }
equivale, para fins de sincronização, a
public static String formatarData() {
synchronized (Util.class) { .... }
}
public static String formatarHora() {
synchronized (Util.class) { .... }
}
ou seja, eles estão sincronizando no mesmo objeto, quando na verdade deveriam sincronizar em objetos diferentes (respectivamente, formatoData e formatoHora).
Até aprender todas essas sutilezas é melhor evitar o uso de synchronized, a menos que seja completamente inevitável. [/quote]
mas ai o problema foi do synchronized e não do static.
[quote=GilsonNunes]
no java tem algo similar a threadvar?[/quote]
Java: ThreadLocal ( http://docs.oracle.com/javase/6/docs/api/java/lang/ThreadLocal.html )
C++ (Microsoft C++): __declspec (thread)
C++ (GCC) : __thread
C++ (Boost): boost::thread_specific_ptr
C#: [ThreadStatic]
Note que [ThreadStatic] é um atributo que se aplica, no C#, a variáveis estáticas. É que nesse caso ele usa o suporte que o Windows dá a esse tipo de armazenagem (“thread local storage”).
No caso do Java, uma variável declarada como ThreadLocal não precisa ser estática. O Java, entretanto, não usa esse suporte que o sistema operacional dá - ele usa uma coisa muito mais simples conceitualmente, que é um mapa Thread -> instância da variável.
[quote=GilsonNunes][quote=entanglement]Resolver resolve, mas eu não gosto de sincronização porque sincronização = serialização, e se você serializar uma operação lenta (formatação de datas) ela pode se tornar um gargalo no sistema.
Dentro da medida do possível, deve-se evitar a sincronização, a menos que isso seja inevitável e você não tenha realmente uma boa alternativa (no meu caso, uma ThreadLocal resolvia bem meu problema, porque transformava o problema de sincronizar uma operação lenta (formatar uma data) no problema de sincronizar uma operação que em média é rápida (escolher no HashMap embutido no ThreadLocal o objeto correspondente à thread corrente).
[/quote]
no java tem algo similar a threadvar?[/quote]
me parece q ja foi respondido.
se entedi bem é o threadLocal
É que normalmente uma coisa leva a outra. Se você for abusar muito do static, vai acabar vendo que para resolver alguns problemas vai precisar ou de um ThreadLocal ou de um synchronized, e por aí vai. Eu só mostrei que saber usar o synchronized não é tão trivial assim quanto parece.
[quote=entanglement][quote=GilsonNunes]
no java tem algo similar a threadvar?[/quote]
Java: ThreadLocal ( http://docs.oracle.com/javase/6/docs/api/java/lang/ThreadLocal.html )
C++ (Microsoft C++): __declspec (thread)
C++ (GCC) : __thread
C++ (Boost): boost::thread_specific_ptr
C#: [ThreadStatic]
Note que [ThreadStatic] é um atributo que se aplica, no C#, a variáveis estáticas. É que nesse caso ele usa o suporte que o Windows dá a esse tipo de armazenagem (“thread local storage”).
No caso do Java, uma variável declarada como ThreadLocal não precisa ser estática. O Java, entretanto, não usa esse suporte que o sistema operacional dá - ele usa uma coisa muito mais simples conceitualmente, que é um mapa Thread -> instância da variável. [/quote]
entanglement, mt obrigado pela resposta.
sou novato no mundo java. mas observando essa discursão sobre o sincronismo, não seria o caso de usar algo imutável?
e não é mesmo. vc ta certíssimo!
eu por exemplo fico “emulando” varias situações aki, pra ver na prática os efeitos desse no java, no intuito de entender melhor.
mas acompanhar essas discursões, aki no Guj, tem sido mt enriquecedor.
grato a tds.
Pois é.
Certas classes (como o SimpleDateFormat) não parecem ser imutáveis, à primeira vista? Eu achava isso até que tive problemas com ela (obviamente eu não havia lido o Javadoc e não tinha visto que ela não era “thread-safe”. )
É que você passa o formato no construtor (e você não pode mudar o formato depois, embora haja setters para mudar algumas outras coisas) e eu só usei, no caso em que houve problemas, para formatar alguma coisa, não para fazer um “parse” - nunca ia pensar que houvesse algum estado interno que fosse dar problemas se o código fosse chamado simultaneamente por duas ou mais threads.
Sim, Gilson, como já falamos no tópico os problemas são quando aparecem variáveis estáticas. Como eu já havia explicado, após o post do Bruno, se o método estático tiver só variáveis locais, nem mesmo sincronização será necessária.
Eu mesmo, na minha primeira afirmação, não falei que métodos estáticos fossem o problema per se, mas sim, que são mais difíceis de se gerenciar em multi-threading. E isso são, pois você terá que cuidar justamente para variáveis estáticas não aparecerem e terá que gerenciar com mais cuidado os locks que eles utilizarem. Ou, acaba enfileirando seu sistema inteiro.
Porém, o tema central do tópico é “utilizar métodos muitos estáticos por todo o programa”. Nesse caso, usar isso como política de desenvolvimento, com a intenção de obter performance, irá promover o aparecimento de variáveis desse tipo. E, consequentemente, estragar um possível paralelismo do sistema, com um resultado muito mais drástico à performance do que o custo de invocação tem.
Se alguém de vocês é um programador Linux/Unix, eu comparo a situação de botar statics no programa inteiro com a de criar um programa que só roda como o usuário “root”, só porque não entendeu como se lida com as permissões de uso dos arquivos e outros recursos.
Isso costuma indicar que o programador não entendeu alguma coisa e ficou com preguiça de correr atrás para entender como é que se fazem as coisas corretamente.