quinta-feira, 21 de abril de 2011

Download de livros

Olá Pessoal, eu vo começar a juntar e postar links de livros que servirão para todos como uma leitura específica sobre os assuntos que estamos estudando em Sistema de informação. Todos São links exclusivos do Blog do SI então baixem a vontade:


Engenharia de Software:

Mcgraw Hill - Software Engine a Practitioners Approach

Sistemas Operacionais:

Ivan Ricarte - Organizacao de Computadores
Andrew Tanenbaum e Albert Woodhull - Sistemas Operacionais: Projeto e Implementação 2 Ed.

Banco de dados:

Carlos Alberto Heuser - Projeto de Banco de Dados

Espero que curtam os downloads, e se preparem que em breve estaremos postando algumas leituras interessantes para complementar o conhecimento de vocês.

[ Confiram também a nossa Pasta virtual! ]

Até a próxima!

PS: Como leitura adicional para os alunos do curso de CBSI 2009 estarei postando um link externo de um livro de Econômia:

VASCONCELLOS M.A. - Economia: Micro e Macro(2006)

quarta-feira, 20 de abril de 2011

Java Threads

Neste post vamos falar um pouco sobre threads Java e escrever códigos simples para visualizar e entender um pouco mais sobre esse assunto. Portanto, o leitor precisa no mínimo saber como executar programas na linguagem Java. Tópicos mais avançados, como sincronização, ficam para outra oportunidade.

Introdução

Observe o código a seguir:
class Sequencial {
    public static void main(String[] args) {
        System.out.println("Thread Atual: " + Thread.currentThread().getName());
        System.out.println("segunda linha");
            if (true) {
                System.out.println("terceira linha");
            }
       System.out.println("quarta linha");
       System.out.println("Thread Atual: " + Thread.currentThread().getName());
    }
}
Quando compilado e executado gera a seguinte saída:

Thread Atual: main
segunda linha
terceira linha
quarta linha
Thread Atual: main

Thread.currentThread() retorna uma referência a thread atual e a chamada getName() usando essa referência, retorna o nome dessa thread. Nesse código, a thread em questão é a thread principal, ou main.

Threads Java são “processos leves”, cada um com sua própria pilha de chamadas. Quando uma nova thread é criada e iniciada, ela é executada paralelamente a thread principal, com sua própria pilha de chamadas. Podemos criar threads de duas formas: herdando java.lang.Thread e sobrescrevendo run() ou criando um objeto do tipo java.lang.Runnable e passando esse objeto ao construtor da classe java.lang.Thread (classes do pacote java.lang são importadas automaticamente).

Herdando java.lang.Thread
class MinhaThread extends Thread {
    @Override
    public void run() {
        System.out.println("funcionalidade da thread aqui");
    }
}
Como Java não suporta herança múltipla, essa forma de criação limita nossa thread porque não podemos herdar outra classe. Para instanciar essa thread, basta usar new normalmente:
Thread t = new MinhaThread();

Implementando java.lang.Runnable
class MeuExecutavel implements Runnable {
    public void run() {
        System.out.println("funcionalidade da thread aqui");
    }
}
Para instanciar uma thread dessa forma, passamos esse objeto Runnable para o construtor da classe Thread:
Thread t = new Thread(new MeuExecutavel());

Iniciando Threads

Com nosso objeto instanciado, podemos iniciar nossa thread usando o método start():

t.start();

Threads java são controladas por uma parte da máquina virtual Java chamada agendador de threads. Portanto, diferentes maquinas virtuais Java podem executar threads de formas diferentes. Se, por exemplo, iniciarmos 3 threads, A, B e C, A não necessariamente terminará antes de B e C, ou B antes de C. Programas portáveis não devem depender de características especificas de máquinas virtuais.

Trabalhando com Threads

Basicamente, o trabalho com threads consiste em criar e iniciar esses objetos, depois coordenar sua execução. Threads possuem estados, descritos a seguir:
  • New: um objeto Thread instanciado e o método start() não foi chamado.
  • Runnable: o método start() foi chamado para o objeto, tornando-o apto a ser escolhido pelo agendador. Não significa que a thread será executada imediatamente.
  • Running: o objeto foi escolhido pelo agendador e sua execução acontece (seu método run() é chamado).
  • Wainting/blocking/sleeping: a execução da thread é suspensa. Para voltar a ser executada, a thread deve primeiro passar pelo estado Runnable.
  • Dead: Uma thread termina quando seu método run() retorna e uma vez terminada, uma thread não pode ser reiniciada (uma exceção em tempo de execução é lançada nesse caso). O objeto thread ainda pode ser usado normalmente.

Vamos escrever uma aplicação para ter uma idéia melhor sobre esse assunto e verificar outras questões, como desempenho.

Medindo escrita em arquivos

Vamos escrever uma aplicação que escreva n linhas em x arquivos e verifique o tempo gasto nessa operação com e sem threads. Detalhes como pacotes, decomposição do problema em classes, etc, serão suprimidos para ajudar o entendimento.

No exemplo a seguir, usamos System.currentTimeMillis() (que retorna a diferença entre o tempo atual e o inicial, 1 de janeiro de 1970, em milisegundos) para calcular intervalos de tempo gastos na execução. Vamos usar System.err no lugar de System.out para tornar mais precisas nossas impressões na linha de comando. O centro desse programa é o objeto CountDownLatch. Esse objeto possui um contador interno, definido em seu construtor, que deve ser decrementado por outras threads. Quando o contador chega a 0, ele libera o bloqueio que foi feito em uma chamada latch.await(). No final, a saída é impressa na linha de comando mostrando o tempo gasto nessa tarefa com e sem threads. Segue o código:
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;

public class Main {
    
    public static void escreverArquivo(String nomeArquivo, int n)
            throws Exception {
        PrintWriter writer = null;
        try {
            writer = new PrintWriter(new FileWriter(nomeArquivo));
            for (int i = 0; i < n; i++) {
                writer.println("Nome " + nomeArquivo + " linha: " + i);
            }
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }

    public static String sequencial(List<String> nomes, int n) throws Exception {
        StringBuilder resultado = new StringBuilder();

        long inicio = System.currentTimeMillis();
        resultado.append("Inicial (ms): ").append(inicio).append("
");
        for (String name : nomes) {
            escreverArquivo(name, n);
        }
        long fim = System.currentTimeMillis();
        long total = fim - inicio;

        resultado.append("Final (ms): ").append(fim).append("
");
        resultado.append("Total (ms): ").append(total).append("
");
        return resultado.toString();
    }

    // nosso objeto runnable
    private static class Tarefa implements Runnable {

        private String name;
        private int n;
        private CountDownLatch latch;

        public Tarefa(String name, int n, CountDownLatch latch) {
            this.name = name; //  nome do arquivo
            this.n = n;
            this.latch = latch;
        }

        @Override
        public void run() {
            try {
                System.err.println("thread processando " + this.name + " iniciada");
                escreverArquivo(this.name, this.n);
                System.err.println("thread processando " + this.name + " finalizada");
                // quando a thread termina sua tarefa, decrementa o contador do objeto bloqueando a thread principal.
                latch.countDown();
            } catch (Exception e) {
                System.err.println(e);
            }
        }
    }

    // faz o mesmo trabalho que o metodo sequencial, mas usando threads.
    public static String thread(List<String> nomes, int n) throws Exception {
        StringBuilder resultado = new StringBuilder();

        CountDownLatch latch = new CountDownLatch(nomes.size());

        long start = System.currentTimeMillis();
        resultado.append("Inicial (ms): ").append(start).append("
");
        for (String name : nomes) {
            
            // uma thread por arquivo a ser escrito
            // depois de iniciadas, cada thread começa sua execução paralelamente
            // a thread que chamou este metodo. Atenção ao objeto latch passado para
            // cada thread.
            new Thread(new Tarefa(name, n, latch)).start(); 
        }
        System.err.println("Esperando threads...");
        
        // thread principal bloqueada esperando pelas errras threads
        // cujo o numero é igual ao numero de arquivos.
        // Quando o contador de latch chegar a 0, o bloqueio e liberado e a execução
        // continua normalmente.
        latch.await();
        System.err.println("threads terminadas.");
        long end = System.currentTimeMillis();
        long total = end - start;
        
        resultado.append("Final (ms): ").append(end).append("
");
        resultado.append("Total (ms): ").append(total).append("
");
        return resultado.toString();
    }

    public static void main(String[] args) throws Exception {
        List<String> nomes = Arrays.asList("arquivo1", "arquivo2", "arquivo3", "arquivo4");
        int linhas = 100000;
        System.err.println("processamento sequencial:");
        System.err.println(sequencial(nomes, linhas));
        System.err.println("processamento com threads");
        System.err.println(thread(nomes, linhas));
    }
}
Compilando e executando o código acima, na minha máquina (a saída pode variar), o resultado foi o seguinte:

processamento sequencial:
Inicial (ms): 1303340796786
Final (ms): 1303340797929
Total (ms): 1143

processamento com threads
thread processando arquivo2 iniciada
Esperando threads...
thread processando arquivo1 iniciada
thread processando arquivo4 iniciada
thread processando arquivo3 iniciada
thread processando arquivo1 finalizada
thread processando arquivo3 finalizada
thread processando arquivo2 finalizada
thread processando arquivo4 finalizada
threads terminadas.
Inicial (ms): 1303340797930
Final (ms): 1303340798327
Total (ms): 397

Fica claro que a execução das threads depende do agendador e que decompor certas tarefas usando threads melhora o desempenho. Nesse exemplo, cada thread é responsável por escrever um arquivo paralelamente as outras.

Leituras adicionais

Thread api: http://download.oracle.com/javase/6/docs/api/java/lang/Thread.html
Capitulo 9 do livro Certificação Sun Para Programador Java 6 Guia de Estudo.

terça-feira, 19 de abril de 2011

Bem Vindos!

Bem-vindos ao blog do CBSI

Pra início de conversa agente criou esse blog pra poder facilitar a  troca de informações que são necessárias para todo e qualquer aluno da área de TI que quer obter êxito em sua formação acadêmica. Então a proposta do blog é postar posts exclusivos e orginais a respeito de temas de nosso interesse como: Programação, Banco de dados, Engenharia de Software, Algoritmo, Tecnologia da Informação dentre outros.

Esperamos que vocês curtam o nosso conteúdo e até o proximo post que deve ser em breve!