Caelum | Ensino e Inovação - Cursos de Java, Scrum, Ruby on Rails


ConcurrentModificationException e os fail-fast iterators

Por Paulo Silveira em 18/08/10

A java.util.ConcurrentModificationException costuma surpreender a muitos: como uma exception com esse nome pode aparecer mesmo em uma aplicação single threaded, que não envolve concorrência alguma no acesso dessa coleção?

Para entender melhor, vale relembrar que as coleções muito antigas, como Vector e Hashtable, são thread safe, implementado através do uso do synchronized em seus métodos e iteradores (Enumeration na época). No Java 1.2, com a entrada das interfaces Collection, List, Set e Iterator, as novas implementações optaram por não ser thread safe, dado o custo de performance que o synchronized apresentava (hoje em dia é muito, muito menor), e de que a grande maioria dos casos de uso dessas estruturas não necessitavam de thread safety.

Essa novidade, das novas coleções não serem preparadas para o uso em um ambiente multi thread, poderia causar surpresas para quem não a estivesse esperando. Em vez de deixar alguém percorrer uma coleção através de um Iterator enquanto ela é modificada concorrentemente (acarretando em dados incorretos, nulls, etc) optou-se por evitar esses casos. Para isso tenta-se “adivinhar” quando houver mais de uma thread trabalhando com a mesma coleção concorrentemente (uma modificando a coleção, e outra iterando-a).

Como fizeram isso sem usar mecanismos de lock? Usando um contador!

Imagine que a cada modificação na estrutura de uma ArrayList, nós incrementamos um contador (modCount). Quando um Iterator é requisitado pelo método iterator(), esse contador é guardado como atributo (expectedModCount). Cada vez que você invocar os métodos next() e remove() (lembrando que temos apenas 3 métodos na interface Iterator), esse iterador vai checar se o contador da ArrayList é exatamente igual ao número que era esperado, isso é, tem o mesmo valor desde quando começamos a percorrer seus elementos. Caso os valores sejam diferentes, a java.util.ConcurrentModificationException é lançada, pois foi detectada uma modificação concorrente (comodification).

Mas como essa exceção pode ocorrer mesmo quando não temos outras threads acessando a mesma coleção? O problema é exatamente essa tentativa de detectar um acesso concorrente:

List<String> nomesDosAlunos = new ArrayList<String>();
// ...  popula lista com strings
for (String nome : nomesDosAlunos) {
	if (nome.equals("nome procurado")) {
		nomesDosAlunos.remove(nome);
	}
}

Esse código vai lançar ConcurrentModificationException caso encontre a String procurada:

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
	at java.util.AbstractList$Itr.next(AbstractList.java:343)
	...

Isso ocorre pois o Iterator utilizado internamente nesse laço vai detectar, na próxima chamada ao seu método next(), que o número de modificações desta ArrayList é diferente de quando ele foi instanciado. Essa stacktrace pode confundir um pouco, já que o uso do enhanced for esconde a utilização do iterator, e não conseguimos ver explicitamente a invocação do método next na linha do for.

Repare que a detecção nesse caso single threaded é arbitrária para evitar casos estranhos (se removessemos o objeto da lista, o iterator deveria ainda percorre-lo?), e coleções sem fail fast iterators vão possibilitar esse tipo de remoção e outras modificações sem lançar excessões. Como então evitar essa exception nas coleções com fail fast iterators? Utilizando o iterator.remove() em vez do enhanced for:

List<String> nomesDosAlunos = new ArrayList<String>();
// ...  popula lista com strings
for (Iterator<String> i = nomesDosAlunos.iterator(); i.hasNext();) {
	String nome = i.next();
	if (nome.equals("nome procurado")) {
		i.remove();
	}
}

Esses iteradores são chamados de fail-fast por possuirem essa característica de falhar quando uma modificação concorrente é detectada. Essa é apenas uma tentativa do Iterator em encontrar possíveis bugs, e existem casos e combinações em que a modificação concorrente pode passar desapercebida por esse mecanismo, e você não deve se basear nessa exception para garantir que sua aplicação é thread safe.

E se precisarmos usar uma lista em ambiente multi thread e percorrer seu iterator enquanto a modificamos? Podemos usar a CopyOnWriteArrayList (nos casos de muita leitura e pouca escrita) ou ainda, se não for precisar de acesso aleatório aos elementos, utilizar uma Queue como a ConcurrentLinkedQueue. Utilizar o antigo Vector ou a própria ArrayList em conjunto com Collections.synchronizedList é thread safe e não vai lançar ConcurrentModificationException desde que você sincronize o uso de seus iterators:

synchronized(nomesDosAlunos) {
	for (String nome : nomesDosAlunos)
		System.out.println(nome);
}

A performance dessas diferentes escolhas pode mudar drasticamente dependendo do contexto de sua aplicação. Pode também não ser necessário utilizar uma coleção que se preocupa com thread safety: algumas vezes basta diminuir o escopo da coleção (pode não ser necessário deixá-la na session, por exemplo) ou ainda trabalhar com cópias defensivas.

  • Share/Bookmark

Possibilidades de design no uso do seu Generic DAO

Por Lucas Cavalcanti em 26/07/10

Muitas vezes, quando estamos criando nosso sistema temos a tentação de criar o GenericDAO para não ter que ficar repetindo as operações CRUD e listagens.

O maior problema com o GenericDAO é que não necessariamente todas as operações fazem sentido para uma determinada classe. Daí o que fazer se, por exemplo, não faz sentido excluir um pagamento?

public class PagamentoDAO
          extends GenericDAO<Pagamento> {

     @Override
     public void excluir(Pagamento pagamento) {
         throw new UnsupportedOperationException();
     }
}

Não parece uma solução muito elegante, mas é um dos únicos jeitos de proibir uma operação declarada na classe mãe, e ainda assim, só funciona em tempo de execução. Esse é um dos principais motivos para muitos não gostarem de usar o GenericDAO e preferirem usar composição ao invés de herança. Mas como fazer para não repetir o código trivial das operações do CRUD?

Um dos jeitos é usar uma outra abstração de persistência de objetos: o Repository. Com o Repository, temos um lugar onde podemos guardar e buscar por objetos, não importando como fazemos isso. O DAO já está muito ligado com armazenamento em banco de dados, e foi criado quando as operações do BD eram muito trabalhosas (em especial no JDBC).

E como juntar o Repository com o GenericDAO? O Repository pode ser definido, por exemplo, como uma interface, e aí podemos fazer o seguinte: se, para um pagamento, faz sentido apenas salvar e listar, mas não excluir, então criamos a interface:

public interface PagamentoRepository {
    void salva(Pagamento pagamento);
    Pagamento busca(Long id);
    List<Pagamento> lista();
}

E usamos o GenericDAO como implementação dessa interface:

class PagamentoDAO
          extends GenericDAO<Pagamento>
          implements PagamentoRepository {
   // implementacao extra
}

E no nosso código de domínio “nunca” referenciaremos o PagamentoDAO, apenas o PagamentoRepository, assim mesmo que a implementação saiba fazer mais coisas, a interface só expõe as operações suportadas.

Se você usa Injeção de Dependências e algum framework que a suporta (como o VRaptor, Spring ou Java EE6), você pode deixar os DAOs apenas como infraestrutura, e usar os Repositories como interfaces públicas da sua aplicação:

public class PagamentoController {
    public PagamentoController(PagamentoRepository repository) {
         this.repository = repository;
    }

    public void salva(Pagamento pagamento) {
         // validações e outras regras
         repository.salva(pagamento);
    }
}

Ainda poderíamos melhorar o nome do nosso repositório para BaseDePagamentos, ContasAPagar ou ainda Pagamentos, evitando usar sufixos nas classes. Usando abstrações e padrões simples conseguimos evitar repetição de código sem perder a semântica e restrições das nossas classes de modelo, além de esconder detalhes de implementação.

  • Share/Bookmark

Compondo seu comportamento: herança, Chain of Responsibility e Interceptors

Por Guilherme Silveira em 28/06/10

São diversos os momentos em que temos a tentação de usar herança para implementar funcionalidades de maneira rápida. Um exemplo simples é o polêmico caso de Properties e Hashtable em Java.

Alguns padrões também costumam ser implementados através de herança são cadeias de responsabilidade, decorators, template method, filtros/interceptadores, entre outros. O exemplo a seguir mostra a adição de comportamento através de herança próxima ao que acontece com uma Servlet, ou ainda um InputStream que delega para outro:

class Server {
  Response service(Request req) {
     return new Response("<html>get para " + req.getPath() + "</html>");
  }
}

class CacheableServer extends Server {
  Response service(Request req) {
     Response resp = super(req);
     resp.addHeader("Cache-control", "public, max-age=7200");
     return resp;
  }
}

class LoggingServer extends CacheableServer {
  Response service(Request req) {
     Logger.debug("Requesting " + req.getPath());
     return super(req);
  }
}

Encadear comportamentos através de herança de classes apresenta diversos problemas de acoplamento e dificulta a customização de um processo.

Se eu desejasse um servidor com log mas sem as características de Cache, deveria criar uma quarta classe, LoggingWithoutCacheServer que herda de Server e reescrever o código de log. Costuma-se então sugerir a utilização de herança para evitar copiar-e-colar de código, mas podemos ver que somente com ela isso não é verdade.

Herança de classes é positivo por causa do polimorfismo, mas cria uma acoplamento perigoso, já discutido por diversos autores, em especial nos livros Effective Java e Design Patterns.

Em Ruby temos uma abordagem semelhante de reutilização de comportamento através da herança de classe ou através da inclusão de módulos:

module ServiceProvider
  def service(req)
    Response.new "<html>get para #{req.path}</html>"
  end
end

module CacheableProvider
  def service(req)
    res = super(req)
    res.headers["Cache-control"] = "public, max-age=7200"
    res
  end
end

module LoggingProvider
  def service(req)
     Logger.debug "Requesting #{req.path}"
     super(req)
  end
end

E agora podemos descrever nosso serviço através da inclusão dos módulos, na ordem que for necessária:

class Server
  include ServiceProvider
  include CacheableProvider
  include LoggingProvider
end

Se desejamos uma outra ordem de execução qualquer, basta mudar a ordem ou adicionar novos módulos que delegam para super quando desejado. Aqui ainda temos problemas de herança apresentados no post de 2006: o acoplamento da classe Server com os módulos é muito forte e, pior ainda, criamos dependências implicitas entre os módulos que antes não se conheciam: se um dos providers de log possui um método chamado info e o outro provider possui outro método info, sua aplicação não funcionará como o esperado.

A inclusão de módulos para compor a herança, seja em tempo de codificação ou execução, mantem o acoplamento alto. Note que ambas as abordagens permitem a adição de novo comportamento ou encadeamento dos mesmos visando minimizar a atualização na classe filha, mas acabando por necessitar que o autor de cada módulo conheça os detalhes dos outros, aumentando o acoplamento.

Favorecer composição de comportamentos através da agregação de objetos diminui o acoplamento entre os mesmos. Em Java teríamos:

interface Server {
  Response service(Request req);
}
class DefaultServer implements Server {
  public Response service(Request req) {
     return new Response("<html>get para " + req.getPath() + "</html>");
  }
}
class CacheableServer implements Server {
  private Server delegate;
  CacheableServer(Server delegate) {
    this.delegate = delegate;
  }
  public Response service(Request req) {
     Response resp = delegate.service(req);
     resp.addHeader("Cache-control", "public, max-age=7200");
     return resp;
  }
}
class LoggingServer implements Server {
  private Server delegate;
  LoggingServer(Server delegate) {
    this.delegate = delegate;
  }
  public Response service(Request req) {
     Logger.debug("Requesting " + req.getPath());
     return delegate.service(req);
  }
}

E a criação de nosso serviço ou processo pode ser totalmente customizada:

Server log = new LoggingServer(new Server());
Server logAndCache =
  new LoggingServer(new CachingServer(new DefaultServer()));

Para quem gostaria de utilizar essa prática de compor o processamento de uma requisição em diversas fases, como as empresas de host, a funcionalidade de Valves do Tomcat em 2004 permitia que criassem filtros, anteriormente possuindo apis não publicadas em sua versão 3, exatamente como aqueles que o middleware Rack faz hoje em dia:

module Rack
  class CustomLog
    def initialize app
      @app = app
    end

    def call env
      status, headers, body = @app.call env
      if env['HTTP_METHOD']=='HEAD'
        body = nil
      end
      [status, headers, body]
    end
  end
end

Em linguages funcionais como Javascript ou com suporte a ponteiros de função, como C, podemos criar o mesmo tipo de composição de comportamento:

server = function (req) {
  // return nova resposta
}

function support_log(base) {
  return function(req) {
    var res = base(req);
    console.log("logando o resultado");
    return req;
  }
}

function support_head(base) {
  return function(req) {
    var res = base(req);
    if(req.method == "HEAD"){
      res.body = "";
    }
    return res;
  }
}

function parse(req) {
  return support_head(support_log(server))(req);
}

Note que em todos os casos, assim como na sequência de converters e wrappers do XStream, favorecemos composição ao invés de herança.

Ao mesmo tempo, encontramos uma dificuldade na hora de implementar a composição: como compartilhar objetos para serem acessados entre diferentes comportamentos que foram adicionados ao nosso objeto original?

A solução mais encontrada em todas as linguagens mencionadas é a criação de um escopo novo contendo todos os objetos a serem compartilhados (contexto), um objeto que funciona como um mapa. Em linguagens com tipagem estática isso pode implicar em um perigo a mais, um cast forçado:

  public Response service(Request req, Context ctx) {
     User user = (User) ctx.get("logged_in_user");
     Logger.debug("Requesting " + req.getPath());
     return delegate.service(req);
  }

Essa é a solução adotada pelo servlet API, além de ser adotado pela implementação do Rack middleware com o env.

Uma outra solução pouco utilizada e que poderia aumentar a legibilidade em troca de menos visibilidade é fazer o lazy load de acesso a variável através do method_missing, algo como:

def method_missing(sym, args)
  return env[sym] if env.include?(sym.to_s)
  super(sym, args)
end

Compor um comportamento através de outros é fundamental para diminuir a complexidade de métodos e classes. Podemos fazer isso através de herança, pagando-se um preço alto, ou utilizar chain of responsability, interceptors e outros padrões, que podem ser implementados puramente através de interfaces e composição.

  • Share/Bookmark

Ordenando coleções com Comparable e Comparator

Por Nico Steppat em 22/06/10

Uma tarefa comum no dia-a-dia dos desenvolvedores é ordenar uma lista ou array. Para não inventar a roda, a Collections API do Java (também conhecida pelo nome do seu pacote, o java.util) vem pronta para ajudar nessa tarefa. Falamos dessa API extensivamente na apostila do curso FJ-11, e vou aqui passar para o problema específico de ordenação.

Imagine que você gostaria de ordenar uma lista de Contas bancárias. Cada conta possui um número (int) e um titular (String):

Conta conta1 = new Conta(5452, "Phillip Lahm");
Conta conta2 = new Conta(1234, "Lucas Podolski");
Conta conta3 = new Conta(3145, "Arne Friedrich");

List<Conta> lista = new ArrayList<Conta>();
lista.add(conta1);
lista.add(conta2);
lista.add(conta3);

O método para ordenar uma lista se encontra na classe java.util.Collections (repare o “s” no final). Ela possui métodos estáticos que ajudam a manipular coleções, entre eles o método sort. Assim podemos tentar ordenar a lista de contas:

Collections.sort(lista);

Mas infelizmente a linha acima nem compila. Antes de invocar o método sort é preciso definir o critério de ordenação: uma forma de informar, dado duas contas, qual vem “antes” e qual vem “depois. Considerando que queremos ordenar pelo número da conta, a classe Conta implementar a interface java.lang.Comparable que define o que será nossa “ordem natural”. A interface possui apenas um método compareTo:

public interface Comparable<T> {
    int compareTo(T outro);
}

As contas então devem ser comparáveis. Vamos definir a ordem natural baseada no número da conta:

public Conta implements Comparable<Conta> {

    private int numero;
    private String titular;
    // outros metodos e atributos

    public int compareTo(Conta outraConta) {
        if (this.numero < outraConta.numero) {
            return -1;
        }
        if (this.numero > outraConta.numero) {
            return 1;
        }
        return 0;
    }
}

Se o número da conta atual é menor do que da outraConta retormamos -1 (ou qualquer int negativo, indicando que this deve vir “antes” de outraConta), se for maior retornamos 1 (ou qualquer int positivo) e se for igual então devolvemos 0.

Agora podemos invocar Collections.sort(lista).

Mas se surgir a necessidade de ordenar pelo titular da conta? Não queremos alterar o método compareTo na classe Conta, já que isso mudaria a ordem natural. Queremos definir um outro critério de ordenação. Para tal, existe uma outra interface: a Comparator:

public interface Comparator<T> {
    int compare(T o1, T o2);
}

Vamos então implementar a interface para definir a ordem pelo titular (String) da conta. Comparar duas Strings é difícil, mas, como você pode imaginar, esse problema já foi resolvido na API do Java. A classe String já sabe compara dois strings, sabemos isso pois ela implementa a interface Comparable (por esse mesmo motivo podemos invocar Collections.sort para uma List de String). Podemos então delegar essa tarefa ao método compareTo das Strings:

public class TitularComparator implements Comparator<Conta> {
    public int compare(Conta conta, Conta outraConta) {
        return conta.getTitular().
                compareTo(outraConta.getTitular());
    }
}

Como escolher para que esse critério de comparação seja utilizado em vez da ordem natural? O método sort é sobrecarregado e pode receber um objeto do tipo Comparator:

TitularComparator comparator = new TitularComparator();
Collections.sort(lista, comparator);

Falta mencionar que o método compareTo da interface Comparable deve ser consistente com o método equals. Quando uma conta é igual a outra (a classe Conta sobreescreve o método equals para definir igualdade), o método compareTo deve devolver zero também. Devemos também pensar se receberemos null como Contas para ordenar, e tomar as devidas precauções nos comparadores.

Através de Comparable e Comparator também são controladas a ordenação da coleção TreeSet e as chaves do mapa TreeMap.

Esse tópico também faz parte da prova de certificação de programador, a SCJP.

  • Share/Bookmark

Metaprogramação: Eigenclass em Ruby

Por Anderson Leite em 10/03/10

O modelo de objetos do Ruby não é trivial. Em códigos mais complexos não é simples saber de onde realmente vem um método ou mesmo enxergar que toda classe em Ruby é um objeto, e isso pode causar algumas confusões. Uma das partes mais interessantes do modelo de objetos Ruby é conhecida como Singleton Class. Para chegar nela, vamos analisar o código abaixo:

class Caelum
  def teste
    puts "teste"
  end
end

obj = Caelum.new
obj.teste

Nada demais até aqui. Apenas criamos uma classe e invocamos um método. Porém em Ruby nossas classes são abertas, e podemos adicionar novos métodos não só em classes já definidas, como também em objetos. Uma forma de fazer isso é:

def obj.ensina
  puts "ruby"
end

obj.ensina # => 'ruby'

Basicamente um objeto procura os métodos na sua classe (obj procura em Caelum) e, caso não encontre, sobe na hierarquia das superclasses, até Object (ou BasicObject no Ruby 1.9).

Mas onde está o método ensina na hierarquia? Ele não pode estar definido em obj, afinal obj não é uma classe, e também não pode estar na classe Caelum, ou todas as instancias da classe Caelum também teriam esse método.

Eigenclasses

O fluxo comentado acima não está totalmente completo. Cada objeto em Ruby pode ter sua própria classe, uma classe especial, escondida. Existe uma sintaxe especial para acessar essa classe, que é:

class << obj
end

Qualquer método definido dentro do código acima pertenceria apenas aquela instância. A classe acima é chamada de Eigenclass (ou Singleton Class). O modelo de objetos Ruby agora fica da seguinte forma:

Eigenclasses e Method Lookup

Seguindo o modelo de objetos Ruby, um objeto procura um método de instância na sua Classe. Caso não ache, sobe para a superclasse da classe.

class Caelum
    def ensina

    end
end

class Sala < Caelum 

end 

obj = Sala.new
obj.ensina

No exemplo acima, obj procura o método ensina na classe Sala. Como não encontra, sobe até a superclasse que é Caelum. Onde entra a Eigenclass nisso ?

Primeiro vamos criar um método em todos os objetos que retorne a Eigenclass de um objeto:

class Object
  def eigenclass
    class << self; self; end
  end
end 

p "caelum".class		#=> String
p "caelum".eigenclass	#=> #Class:#String:0x18ba78

Repare que o Ruby coloca um sinal # na frente da Eigenclass. Usaremos esse sinal apartir de agora para identificar esse tipo de classe, sendo #obj a Eigenclass do objeto obj.

Agora vamos criar um método em obj colocando-o na sua Eigenclass e verificar a hierarquia:

class << obj
  def eigen_method
    'obj#eigen_method()'
  end
end 

p obj.class				#=> Sala
p obj.eigenclass			#=> #obj
p obj.eigenclass.superclass	#=> Sala

A superclasse de uma Eigenclass é a própria classe do objeto. Ou seja, se um objeto possui uma Eigenclass, Ruby inicia a procura do método nela. Caso não encontre, sobe para a primeira superclasse (que é a própria classe do objeto).

Eigenclasses e herança

Para completar o modelo, quem é a superclasse de uma Eigenclass? Vamos adicionar uma eigenclass na classe Caelum e analisar o modelo:

class Caelum
    class << self
      def meu_eigen_method
        'Caelum.meu_eigen_method()'
      end
    end
end      

p Caelum.eigenclass				# => #Caelum
p Sala.eigenclass				# => #Sala
p Sala.eigenclass.superclass		# => #Caelum
p Caelum.eigenclass.superclass	# => #Object

Repare que a Superclass da Eigenclass de Sala (Sala.eigenclass.superclass) é a eigenclass da classe Caelum! Ou seja, A superclasse de uma eigenclasse é a eigenclass da superclasse do objeto! Não muito trivial.

Matz, o criador do Ruby não chegou a definir um nome para esse tipo de classe, sendo que os programadores costumaram chamá-la de singleton class por conta própria. Como muita gente acabou confundindo com o design pattern de mesmo nome, outras opções começaram a surgir. Eingenclass surgiu na Alemanha onde eigen tem o significado de “próprio de algo”, sendo uma tradução comum algo como “a própria classe do objeto”.

  • Share/Bookmark



Caelum | Ensino e Inovação
São Paulo: Rua Vergueiro, 3185, cj. 87, próximo ao Metrô Vila Mariana   |   Tel. (11) 5571-2751
Rio de Janeiro: Rua Senador Dantas, 80, cj. 307/308 - Centro   |   Tel. (21) 2220-4156 ou 2297-0033
Brasília: SCS Qd. 8 Bl. B-50, Sala 521 - Ed. Venâncio 2000   |   Tel. (61) 3039-4222