Todas las entradas de: xavsal

OCP 11 – Language Enhancements (Java Fundamentals – Final modifier)

Introduction

Final modifier can be applied to variables, methods and classes.
Marking a:

  1. Variable final means the value cannot be changed after it is assigned.
  2. Method or a class means it cannot be overridden (for methods) or extended (for classes).

Declaring final local variables

For final variables there are several aspects to consider.

We do not need to assign a value to the final variable when we declare it. What we have to assure is the a value has been assigned to it before this final variable is used. We will get a compilation error in case we don’t follow this rule. Example which illustrates this:

private void printZooInfo(boolean isWeekend) {
    final int giraffe = 5;
    final long lemur;
    if (isWeekend) lemur = 5;
    giraffe = 3; // DOES NOT COMPILE   
    System.out.println(giraffe+" "+lemur); // DOES NOT COMPILE
}

Here we have two compilation errors:

  1. The giraffe variable has an assigned value so we can’t assign a new value because it has been declared as final. We will get a compilation error.
  2. When attempting to use lemur variable we will get a compilation error. If condition isWeekend is false we can’t assign the value to lemur so we will the error the error compilation because a local variable to has to be declared and assigned before using it (despite the fact of being declared as final or not).

When we mark a variable as final it does not mean that the object associated with it cannot be modified. Example to illustrate this:

final StringBuilder cobra = new StringBuilder();
cobra.append("Hssssss");
cobra.append("Hssssss!!!");

We have declared the variable as constant but the content of the class can be modified.

Adding final to Instance and static variables

Instance and static class variables can be marked as final too.

When we mark as final a:

  1. Instance variable which it means that it must be assigned a value when it is declared or when the object is instantiated (Remember: We can only assign once, like Local Variables). Example to illustrate this:

    public class PolarBear {
    final int age = 10;
    final int fishEaten;
    final String name;
    
    { fishEaten = 10; }
    
    public PolarBear() {
      name = "Robert";
    }
    public PolarBear(int height) {
      this();
    }
    }

    Does this code compile? Yes. Everything. Exercise: Explain why.

  2. Static variable which it means they have to use static initializers instead of instance initializers. Example to illustrate this:

    public class Panda {
    // We assign a value when we declare the final variable
    final static String name = "Ronda";
    static final int bamboo;
    static final double height; // DOES NOT COMPILE - Why? Because we do not have assign any value to height variable  
    // It will work because we are initializing a final static variable through an static initializer
    static { bamboo = 5;}}

Writing final methods

Methods marked as final cannot be overriden by a subclass. This avoids polymorphic behavior and always ensures that it is always called the same version method. Be aware because a method can have abstract or final modifier but not both at the same time.

When we combine inheritance with final methods we always get an error compilation.

We cannot declare a method final and abstract at the same time. It is not allowed by the compiler and of course we will get a compilation error.
Example to illustrate this:

abstract class ZooKeeper {   
    public abstract final void openZoo(); // DOES NOT COMPILE
}

Marking Classes final

A final class is one class that cannot be extended. In fact we will get a compilation error if we tried. Example to illustrate this:

public final class Reptile {}
public class Snake extends Reptile {} // DOES NOT COMPILE

We cannot use abstract and final modifiers at the same time.

public abstract final class Eagle {} // DOES NOT COMPILE

It also happens the same for interfaces.

public final interface Hawk {} // DOES NOT COMPILE

We will get a compilation error in both cases.

OCP11 – Local Variable Type Inference

Working with Local Variable Type Inference

After Java 10 we can use the keyword var instead of the type for local variables (like the primitive or the reference type) under certain conditions within a code block.

public void whatTypeAmI {
    var name = "Hello";
    var size = 7;
}

The formal name of this feature is local variable type inference but we have to consider two main parts for this feature.

Seguir leyendo OCP11 – Local Variable Type Inference

Sdkman – The Software Development Kit Manager

SDKMAN! es una herramienta para manejar versiones paralelas de múltiples Kits de Desarrollo de Software en la mayoría de los sistemas basados en Unix. En este post, aunque originalmente está pensando para sistemas Unix veremos su utilización mediante Java en su version 11 en entornos Windows, concretamente con Windows 10.

Proporciona una conveniente Interfaz de Línea de Comando (CLI) y API para instalar, cambiar, eliminar y listar candidatos.

Anteriormente conocido como GVM el Groovy enVironment Manager, fue inspirado por las muy útiles herramientas RVM y rbenv, utilizadas en general por la comunidad Ruby.

Para poder ser utilizando en entornos Windows es neceasario realizar una refacorización del código fuente del script bash original.

Existe una sección (install) en la propia página web dónde se indican los pasos a seguir para su instalación y utilización en entornos Windows.

Para mostrar su uso en Windows 10 se utiliza el Shell de Git Bash mediante el uso de este script get_sdkman_io (ya preparado para funcionar en entornos Windows).

Seguir leyendo Sdkman – The Software Development Kit Manager

OCP7 11 – Hilos (09) – E/S Simultánea

Las llamadas de bloqueo secuencial se ejecutan en una duración de tiempo más larga que las llamadas de bloqueo simultáneo.

E/S Simultánea
E/S Simultánea

Reloj: Existen diferentes formas de medir el tiempo.

En el gráfico se muestran cinco llamadas secuenciales a servidores de red que tardarán aproximadamente 10 segundos si cada llamada dura 2 segundos.

En la parte derecha del gráfico, cinco llamadas simultáneas a los servidores de red solo tardan un poco más de 2 segundos si cada llamada dura 2 segundos.

Ambos ejemplos usan aproximadamente la misma cantidad de tiempo de CPU, la cantidad de ciclos de CPU.

Seguir leyendo OCP7 11 – Hilos (09) – E/S Simultánea

OCP7 11 – Hilos (07) – Fork Join

Necesidad de un marco Fork-Join

La división de conjuntos de datos en subconjuntos con el mismo tamaño para cada thread de ejecución tiene un par de problemas.

Marco Fork-Join
Necesidad de un marco Fork-Join

Lo ideal es que todas las CPU se utilicen completamente hasta que la tarea finalice pero:

  • Las CPU se pueden ejecutar a diferentes velocidades.
  • Las tareas que no son de Java requieren tiempo de CPU y pueden reducir el tiempo del que dispone un thread de Java para la ejecución en una CPU.
  • Los datos que se analizan pueden requerir diferentes cantidades de tiempo para el proceso.

Seguir leyendo OCP7 11 – Hilos (07) – Fork Join

OCP7 11 – Hilos (08) – Paralelismo

Introducción

Los sistemas modernos contienen varias CPU. Para sacar partido de la potencia de procesamiento en un sistema es preciso ejecutar tareas en paralelo en varias CPU.

  • Divide y vencerás.
    Una tarea se debe dividir en subtareas. Debe intentar identificar aquellas subtareas que se puedan ejecutar en paralelo.

  • Puede ser difícil ejecutar algunos problemas como tareas paralelas.

  • Algunos problemas son más sencillos. Los servidores que soportan varios clientes pueden usar una tarea independiente para manejar cada cliente.

  • Se debe tener cuidado con el hardware. La programación de demasiadas tareas paralelas puede afectar de forma negativa al rendimiento.

Recuento de CPU

Si las tareas requieren muchos cálculos, al contrario de operaciones que generan muchas E/S, el número de tareas paralelas no debe superar en gran cantidad el número de procesadores del sistema.

Puede detectar el número de procesadores de forma sencilla en Java:

int count = Runtime.getRuntime().availableProcessors();

Sin paralelismo

Los sistemas modernos contienen varias CPU. Si no aprovechan los threads de alguna forma, sólo se utilizará una parte de la potencia de procesamiento del sistema.

Definición de etapa

Si tiene una gran cantidad de datos que procesar pero solo un thread para procesar dichos datos, se utilizará una CPU.

Como ejemplo el procesamiento de una matriz podría ser una tarea simple, como buscar el valor más alto en la matriz. Entonces, en un sistema de cuatro CPU, debe haber tres CPU inactivas mientras se procesa la matriz.

Paralelismo naive

Una solución paralela simple divide los datos que se van a procesar en varios juegos en un juego de datos para cada CPU y un thread para procesar cada juego de datos.

División de datos

Siguiendo el ejemplo de la matriz (como gran juego) se divide en cuatro subjuegos de datos, uno para cada CPU.

Ello implica que se crea un thread por CPU para procesar los datos.

Tras el procesamineto de los subjuegos de datos, los resultados se tendrán que combinar de una forma significativa.

Hay distintas forma de subdividir el juego de datos grande que se va a procesar.

  • Se usaría demasiada memoria para crear una matriz por thread que contenga una copia de una parte de la matriz original.

  • Cada matriz puede compartir una referencia a una única matriz grande pero solo acceder a un subjuego de una forma con protección de hread no bloqueante.

Discusión: Fork vs Branch en Git

Conceptos

La diferencia conceptual entre forking y branching viene dada por desarrollo divergente vs convergente:

  • Concepto de Forking
    Se refiere al proceso de generar una copia exacta del repositorio origen a uno nuevo en ese instante temporal. Es una copia física real y diferente, la operativa surge para realizar separaciones reales y crear nuevas lógicas bajo una base común, se asume que es poco probable que vuelvan a reunirse con el parent.

  • Concepto de Branching
    Se refiere a generar un copia del repositorio dentro del mismo repositorio origen, un pointer. Las ramas son espacios temporales sobre los que cuales realizar desarrollos nuevos o cambios. Su objetivo es volver a converger con el repositorio siempre.

La diferencia conceptual es el scope de la copia (copia separada del parent o dentro de éste) y vida (vida independiente contra tiempo de vida efímero).

La razón de la diferencia parece ser la necesidad de controlar quién puede o no realizar push de código a la rama principal, la práctica del forkeo es más común en el open source cuando los posibles colaboradores no tienen permisos sobre el repositorio original, de ahí la copia que genera otro repositorio de facto y luego la posibilidad de converger con el parent o no.

Diferencias

  1. Forking es más costoso al tener que comparar dos codebases una contra la otra, ya que el fork representa una copia literal del repositorio original (doble de espacio de almacenamiento).

  2. Branching sólo añade una rama sobre el árbol actual, el tamaño de la rama viene ligada literalmente a los cambios de ésta.

  3. Forking ofusca más ver en que anda trabajabando el equipo, al tener que moverse entre repositorios distintos en vez de sobre ramas sobre uno solo repositorio.

  4. Forking al no ser un workflow colaborativo, los cambios residen en la copia de cada uno y puede llevar a mayores problemas a la hora de mergear, perecer (políticas internas de la casa para autoborrado de forks por falta de uso para liberar espacio…) o pérdida de conocimiento.

  5. Branching al centralizar el workflow sobre un sólo repositorio permite, al actualizar sus copias, 1 remote recibir el estado de todos los remotos de las features de sus compañeros.

Why?

The Bitbucket team recommends branching for development teams on Bitbucket.

— Bitbucket

[…]People refer to Git’s branching model as its “killer feature,” and it certainly sets Git apart in the VCS community. Why is it so special? The way Git branches is incredibly lightweight, making branching operations nearly instantaneous, and switching back and forth between branches generally just as fast. Unlike many other VCSs, Git encourages workflows that branch and merge often, even multiple times in a day.

Pro Git Book by Scott Chachon and Ben Straub

Aparte de una diferencia de estilo de trabajo, de que conceptualmente los forks son para otra cosa, y el coste real es espacio en disco y tiempo de copia… en realidad ambas operativas son similares e incluso complementarias, no excluyentes.

Razón

La razón para utilizar únicamente branches es:

  1. Una forma más rápida y cómoda de recorrer el código (1 repositorio, 7 ramas de feature; en vez de bajarse 7 forks y sin saber si existirán más ramas dentro del fork).
  2. Implementar GitFlow o una aproximación a éste en nuestra forma de trabajar de forma más realista.

Requerimientos

Para poder empezar a trabajar con branches sin repercutir en la productividad deben tenerse en cuenta los siguientes pasos:

  1. Plan fijado de sobre como implementar la CI en las branches.
    • Protección de ramas a commit de developers.
      • hotfix branches, quién, cómo se crean y cierran.
      • release branches.
      • master es productivo.
      • preproducción queda atada a release branches o se puede añadir una branch específica para preproducción sobre cada release.
  2. Pipelines de la CI/CD.
    • Activación automática de la CI en branches.
      • En vez de activación por commit en master de los forks, que se activen también por commit en branches.
      • Para reducir ejecuciones podría restringirse la ejecución manual de la CI en las ramas feature.

Refs

Se puede descargar este post mediante este enlace sod_branching-vs-forking

OCP7 11 – Hilos (04) – ReentrantReadWriteLock

Nota: Los ejemplos mostrados en este artículo suponen la utilización de arquitecturas de CPU que soportan operaciones de definición y comparación atómicas (operaciones Compare And Swap), como por ejemplo los procesadores x86 o Sparc actuales. En este caso las operaciones lock / unlock serán operaciones no bloqueantes. Otras arquitecturas que no soporten esta funcionalidad pueden requerir alguna forma de bloqueo interno por parte de la plataforma.

Paquete java.util.concurrent.locks

El paquete java.util.concurrent.locks es un marco para bloquear y esperar condiciones que es distinto de las supervisiones y sincronización incorporadas.

Bloqueo de varios lectores y un único escritor

    public class ShoppingCart {
    private final ReentrantReadWriteLock rw1 = new ReentrantReadWriteLock();

    public void addItem (Object o) {

    rw1.writeLock().lock();
    //  source code to modify shopping cart
    rw1.writeLock().unlock();
    }
    }  // End class ShoppingCart

El código fuente en main() que implementa el bloqueo y el desbloqueo es el siguiente junto con el código fuente para realizar las modificaciones del shopping cart:

    rw1.writeLock().lock();
    //  source code to modify shopping cart
    rw1.writeLock().unlock();

realizando el bloqueo de escritura.

Describiendo un poco todo lo documentado, una de la funciones del paquete java.util.concurrent.locks es la implantación de un bloqueo de varios lectores con un único escritor.

Es posible que un thread no tenga ni obtenga un bloqueo de lectura mientras está en uso el bloqueo de escritura.

Varios threads pueden adquirir simultáneamente el bloqueo de lectura pero sólo uno de ellos puede adquirir el bloqueo de escritura (n -lecturas <-> 1 escritura).

El bloqueo es reentrante: un thread que ya adquirido el bloqueo de escritura puede llamar a métodos adicionales que también obtengan el bloqueo de escritura sin miedo a que se produzca un bloqueo.

Bloqueo de lectura (sin ningún escritor)

Ejemplo dónde se muestra como los métodos de lectura son concurrentes:

    public class ShoppingCart {
        public String getSummary() {
            String s = "";
            rw1.readLock().lock();
            //  Source code to read cart, modify s
            rw1.readLock().unlock();
            return s;
        }

        //  Todos los métodos de sólo lectura se pueden ejecutar de forma simultánea
        public double getTotal () {
            //  another read-only method
        }
    }   //  End class ShoppingCart

En el ejemplo todos los métodos determinados como de sólo lectura pueden agregar el código necesario para bloquear y desbloquear un bloqueo de lectura.

ReentrantReadWriteLock permite la ejecución simultánea de ambos, dónde puede ejecutar un único método de sólo lectura o varios métodos de sólo lectura concurrentes.

CRLF end of line problems in Git

Sometimes a new problem could appear regarding the end of line on files using an IDE (like IntelliJ for example) when doing a Commit&Push into Master through Git.

When this situation happens a new window may popup:

IntelliJ Line Separators Warning

It is recommended the use of the option "commit as it is" by default.

Be sure to uncheck the setting ‘Warn if CRLF line separators are about to be commited to avoid the warning popup‘ in case of using the IntelliJ IDE.

To prevent git from automatically changing the line endings on your files in general is enough running this command:

git config --global core.autocrlf false

BUt a general solution that force one customized configuration is the creation of a new file in the root folder of the project called .gitattributes.

This is its content:

* text eol=crlf working-tree-encoding=UTF-8
*.java text eol=crlf working-tree-encoding=UTF-8
*.xml text eol=crlf working-tree-encoding=UTF-8
*.properties text eol=crlf working-tree-encoding=UTF-8
*.jsp text eol=crlf working-tree-encoding=UTF-8
*.sql text eol=crlf working-tree-encoding=UTF-8

It’s important to point out that this configuration can be changed and adapted to a different one depending on the necessities of the project.

More references

  1. Git documentation here

  2. Git attributes depending on the programming language can be found here.