Mostrando las entradas con la etiqueta java. Mostrar todas las entradas
Mostrando las entradas con la etiqueta java. Mostrar todas las entradas

domingo, 20 de abril de 2008

¿Cómo funciona synchronized en Java?

Mucha gente que usa Java no tiene claro cómo funciona realmente la palabra clave synchronized. Voy a intentar explicarlo acá porque es realmente muy fácil si se tienen en cuenta algunas cosas.

En primer lugar, usar synchronized en un método de instancia es lo mismo que poner un bloque de synchronized(this){} que contenga todo el código del método. Es decir,
public synchronized void metodo() {
   // codigo del metodo aca
}
es lo mismo que
public void metodo() {
   synchronized(this) {
      // codigo del metodo aca
   }
}
Si el método es de clase entonces es lo mismo pero el bloque de synchronized se aplica a la clase. Ejemplo, si el método está en la clase MiClase entonces esto
public static synchronized void metodo() {
   // codigo del metodo aca
}
es lo mismo que
public static void metodo() {
   synchronized(MiClase.class) {
      // codigo del metodo aca
   }
}


Habiendo dicho esto, el problema se reduce a entender cómo funciona el synchronized de un bloque porque, como vimos, el synchronized en métodos de instancia o de clase son simplemente casos especiales de esto.

El bloque synchronized lleva entre paréntesis la referencia a un objeto. Cada vez que un thread intenta acceder a un bloque sincronizado le pregunta a ese objeto si no hay algún otro thread que ya tenga el lock para ese objecto. En otras palabras, le pregunta si no hay otro thread ejecutando algun bloque sincronizado con ese objeto (y recalco que es ese objeto porque en eso radica la clave para entender el funcionamiento)
Si el lock está tomado por otro thread, entonces el thread actual es suspendido y puesto en espera hasta que el lock se libere. Si el lock está libre, entonces el thread actual toma el lock del objeto y entra a ejecutar el bloque. Al tomar el lock, cuando venga el proximo thread a intentar ejecutar un bloque sincronizado con ese objeto, será puesto en espera.
¿Cuándo se libera el lock? Se libera cuando el thread que lo tiene tomado sale del bloque por cualquier razón: termina la ejecución del bloque normalmente, ejecuta un return o lanza una excepción.

Es importante notar una vez más que el lock es sobre un objeto en particular. Si hay dos bloques synchronized que hacen referencia a distintos objetos (por más que ambos utilicen el mismo nombre de variable), la ejecución de estos bloques no será mutuamente excluyente.

Conclusiones:

De todo esto se puede concluir que un método synchronized de instancia puede ser ejecutado al mismo tiempo que uno de clase.
Además, dos threads pueden ejecutar el mismo método de instancia al mismo tiempo aunque este sea synchronized si la invocación se hace a dos objetos diferentes.
Por último, todos los métodos synchronized de instancia son mutuamente excluyentes entre sí. Es decir que, dado un objeto compartido por más de un thread, sólo uno de ellos puede acceder en un determinado momento a uno de esos métodos de instancia. Los otros threads deberán esperar aunque hayan querido acceder a otro de los métodos (porque todos están synchronizados con el mismo this).

Puede encontrarse mucho más info del tema en este tutorial de sun (en inglés).

martes, 4 de diciembre de 2007

Eclipse Quick Access (aka Ctrl + 3)

Ya que les estuve comentando algo de Eclipse Europa en el post anterior, les paso otro tip. Este me lo pasó mi amigo Claudio.

Se trata de la madre de todos los atajos. Si te gusta usar atajos de teclado, entonces probá Ctrl + 3 en Eclipse Europa.

Con eso se abre la ventana del Quick Access. Permite acceder a cualquier Vista, Perspectiva, Comando, Editor, etc con sólo tipear su nombre (o parte de él, porque, por supuesto, al igual que el buscador de clases Ctrl+Shif+T, soporta caracteres comodines, escribir sólo las iniciales de lo que se busca, etc, etc).

Básicamente, se puede acceder a casi todo con el Ctrl+3!!!

miércoles, 26 de septiembre de 2007

Save Actions en Eclipse Europa

A esta altura, esta característica de Eclipse Europa no es nada nueva, pero vale la pena comentarla.

A partir de la versión Europa de Eclipse existe la posibilidad de definir ciertas acciones a ejecutar automáticamente cada vez que se grabe un archivo.

Yo en general sólo uso las que están marcadas en la figura, que te autoformatean el código (como hacer Ctrl+Shif+F, digamos) y te organizan los import (análogo a Ctrl+Shif+O).


Pero hay un montón de cosas que se pueden configurar como Additional Actions. En la figura de abajo se ven algunas, pero esta es la lista simplificada (para más detalles mirá directamente en Eclipse que está todo muy claro):
  • Que automáticamente ponga o no bloques en los if/else/for/while.
  • Automáticamente convertir los for en foreachs (cuando se pueda, claro).
  • Rodear las condiciones (o no) con paréntesis.
  • Hacer que automáticamente se agregue la palabrita final a los métodos, variables privadas y/o variables locales.
  • Usar o no el this para acceder a métodos y variables de instancia.
  • Forzar que automáticamente se use el nombre de la clase cuando se utilicen métodos o campos estáticos.
  • Remover automáticamente miembros privados que no sean usados (ojo con este porque te hace desaparecer las cosas si las dejaste de usar temporalmente).
  • Remover casteos innecesarios
  • Agregar automáticamente las anotaciones @Deprecated y @Override.
  • Remover espacios en blanco al final de las líneas.
  • Ordenar los métodos (de acuerdo a cómo se configure en Members Sort Order)


Eso es todo, ojalá les sirva!

viernes, 6 de julio de 2007

¿Por qué la gente hace e.printStackTrace() en vez de relanzar como RuntimeException?

¿No les pasó de ver código similar a este?
try {
    InputStream input = new FileInputStream("myFile.txt");
} catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
Seguro que sí. Desde verlo en ejemplos en libros de texto (porque "total, es un ejemplo, se entiende que no es lo correcto") hasta en códigos de programas reales. ¡Pero si hasta eclipse autogenera ese try-catch!

Es una porquería. No sirve para nada y oculta bugs. La razón más común por la que se lo hace creo que es porque no se quiere manejar la excepción. Esto puede ser porque ese error nunca ocurre, porque no quiero que mi método tenga que tener un throws, o por desconocimiento, no se.

La forma correcta es esta:
try {
    InputStream input = new FileInputStream("myFile.txt");
} catch (FileNotFoundException e) {
    e.printStackTrace(); // esto es opcional, obviamente
    throw new RuntimeException(e);
}
De esta manera, al relanzar la excepción como RuntimeException nuestro método no tiene que declarar throws FileNotFoundException (para este ejemplo, claro está) y si el error ocurre lo vamos a ver enseguida porque nuestra aplicación va a fallar con un hermoso stack trace.

Podemos hacer que eclipse nos ayude con esto, modificando el code template. Vamos a Windows->Preferences->Java->Code Style->Code Templates y buscamos el que se llama Catch Block Body.

El valor por default es:
// ${todo} Auto-generated catch block
${exception_var}.printStackTrace();
Yo sugiero cambiarlo a:
${exception_var}.printStackTrace();
throw new RuntimeException(${exception_var});

Ah, de paso les comento, con respecto a lo que comenté hace un tiempo, que ya salió el Eclipse Europa.

Disfruten!

miércoles, 13 de junio de 2007

Se viene el nuevo Eclipse Europa

The Next Total Eclipse
Así como el año pasado salió el Eclipse Callisto, este año sale el Eclipse Europa. Como dice en el sitio web, Europa es la salida en simultáneo de 21 proyectos con el objetivo de eliminar las posibilidades de incompatibilidades entre ellos.

Faltan 16 días...

sábado, 26 de mayo de 2007

HTML a PDF y otras transformaciónes con Open Office como Server

El problema

Hace unos días me vi forzado a buscar una manera de convertir documentos HTML a PDF por un requerimento en el laburo. Los prerrequisitos eran: tiene que ser algo que se pueda hacer desde Java y que no haya que pagar ninguna licencia.

Primera aproximación

Comencé mi búsqueda en Google con cosas como html2pdf java. Más allá de que era complicado encontrar algún resultado copado, la mayoría de las estrategias que aparecían iban por el lado de:
  1. Convertir el HTML a XHTML (por ejemplo con JTidy)
  2. Convertir el XHTML a XSL-FO (Extensible Stylesheet Language Formatting Objects) usando una hoja de estilo XSL y un trasformador XSLT (por ejemplo con Xalan)
  3. Formatear el XSL-FO para finalmente generar un PDF (por ejemplo con FOP)
Esta guía explica muy bien cómo seguir esta estrategia. El asunto es que FOP aún no soporta por completo el estandar de XSL-FO y los PDF generados mediante esta técnica no quedan nada bien.

Cambio de estrategia

Juan me comentó acerca de este post en PC++ que hablaba de la posibilidad de usar a Open Office en modo servidor y enviarle mensajes mediante un socket. En esos mensajes se le podían decir cosas tan geniales como "abrí este archivo y exportalo a PDF". El post hablaba de usar un script en python, pero tenía que haber una manera de ejecutar eso desde Java (sin llegar a JPython).

Efectivamente, en la página de la API de Open Office encontré que está disponible para Java, C++ y Python.

La Solución

Luego de lidiar un rato con la API de Open Office dije "tiene que haber algo más fácil". Fue así como llegué a JODConverter (Java Open Document Converter).

JODConverter es una bibiloteca que encapsula las dificultades de conexión a Open Office que impone el uso de la API oficial y hace que su uso sea tan simple como esto:
  // it will guess the format based on the file extensions
File inputFile = new File("document.doc");
File outputFile = new File("document.pdf");

// connect to an OpenOffice.org instance running on port 8100
OpenOfficeConnection connection = new SocketOpenOfficeConnection(8100);
connection.connect();

// convert - input and output formats are guessed from file extensions
DocumentConverter converter = new OpenOfficeDocumentConverter(connection);
converter.convert(inputFile, outputFile);

// close the connection
connection.disconnect();
JODConverter tiene pocas dependencias, pero está en el mvnrepository, así que si usás maven vas a salir andando en 5 minutos.

Configurando Open Office

Y sí, algo hay que hacer para que Open Office responda mis pedidos de conversión de archivos, pero también es muy simple. Hay que iniciarlo en modo servidor y decirle que escuche en algún puerto (para estos ejemplos, uso el puerto 8100, pero cualquiera que esté libre debería servir). Suponiendo que el ejecutable es soffice:
soffice -headless -accept="socket,port=8100;urp;"
Issue conocido

Hay un problema reportado en Open Office que impide que el servidor de Open Office pueda correr en una computadora diferente a la que lo invoca.

En el ejemplo de más arriba usamos a la clase SocketOpenOfficeConnection. Esta clase lo que hace es usar el socket con Open Office para darle las instrucciones, pero el archivo se pasa por referencia y Open Office debe tener permiso para abrirlo del Filesystem (y permiso de escritura para guardar el resultado). Existe otra clase llamada StreamOpenOfficeDocumentConverter que envía y recibe el contenido de los archivos por socket. Esta clase es la que no funciona bien por el bug de Open Office.


Espero que a alguien le sirva toda esta data :D