Threads em Java
Threads em Java
Sumrio a
1 Introduo ca 1.1 O que so threads? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . a 1.2 Todo programa em Java multithreaded . . . . . . . . . . . . . . . . . . . e 1.3 Por que utilizar threads? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Utilizando threads 2.1 Criando threads . . . . . 2.2 Poss veis estados de uma 2.3 Prioridades de threads . 2.3.1 Fatias de tempo 1 1 2 2 2 2 4 5 5 6 7 8 9 9 11
. . . . thread . . . . . . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
3 Sincronismo entre threads 3.1 Utilizando locks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2 Variveis volteis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . a a 3.3 Quase todas as classes no so sincronizadas . . . . . . . . . . . . . . . . . a a 4 Os mtodos wait(), notify() e notifyAll() e 5 Mais Informaes co
1 Introduo ca
Este seo fornece uma breve introduo sobre threads adaptada do artigo Introduction ca ca to Java Threads [4].
Threading um artif e cio que permite a coexistncia de mltiplas atividades dentro e u de um unico processo. Java a primeira linguagem de programao a incluir explici e ca tamente o conceito de Threads na prpria linguagem. Threads so tambm chamados o a e de processos leves (lightweight processes) pois, da mesma forma que processos, threads so independentes, possuem sua prpria pilha de execuo, seu prprio program counter a o ca o e suas prprias variveis locais. Porm, threads de um mesmo processo compartilham o a e memria, descritores de arquivos (le handles) e outros atributos que so espec o a cos daquele processo. Um processo pode conter mltiplas threads que parecem executar ao mesmo tempo u e de forma ass ncrona em relao as outras threads. Todas as threads de um mesmo ca processo compartilham o mesmo espao de endereamento de memria, o que signica c c o que elas tm acesso as mesmas variveis e objetos, e que eles podem alocar novos objetos e a de um mesmo heap. Ao mesmo tempo que isso torna poss que threads compartilhem vel informaes entre si, necessrio que o desenvolvedor se assegure que uma thread no co e a a atrapalhe a execuo de outra. ca A API de threads em Java incrivelmente simples. Mas escrever um programa come plexo que as use de forma eciente e correta no to simples assim (esta uma das a e a e responsabilidade do programador impedir que uma razes da existncia de MAC 438). E o e thread interra de forma indesejvel no funcionamento das outras threads do mesmo proa cesso.
Processamento ass ncrono ou em segundo plano: com threads um servidor de e-mail pode, por exemplo, atender a mais de um usurio simultaneamente. a
2 Utilizando threads
2.1 Criando threads
H duas formas equivalentes de criarmos uma thread em Java. Ou criamos um objeto a que estende a classe Thread e sobrescrevemos o seu mtodo public void run() ou e implementamos a interface Runnable que denida como: e package java.lang; public interface Runnable { public abstract void run(); } O exemplo abaixo ilustra a utilizao de threads utilizando as duas formas. ca public class Exemplo extends Thread { // classe interna que estende thread // (a classe definida como static apenas para podermos e // alocar uma inst^ncia no main) a static class Tarefa1 extends Thread { // mtodo que sobrescreve o mtodo run() herdado e e public void run() { for(int i=0; i<1000; i++) { System.out.println("Usando Herana"); c } } } // classe interna que implementa a interface Runnable static class Tarefa2 implements Runnable { public void run() { for(int i=0; i<1000; i++) { System.out.println("Usando Runnable"); } } } public static void main(String[] args) {
// basta criarmos uma int^ncia da classe que extende Thread a Thread threadComHeranca = new Tarefa1(); // primeiro devemos alocar uma inst^ncia de tarefa a Tarefa2 tarefa = new Tarefa2(); // e depois criamos a nova Thread, passando tarefa como argumento Thread threadComRunnable = new Thread(tarefa); // agora iniciamos as duas Threads threadComHeranca.start(); threadComRunnable.start(); } } A sa do texto, como esperado, so as duas mensagens intercaladas aleatoriamente da a na sa padro. Dependendo do sistema operacional e dos recursos computacionais do da a computador a sa pode parecer seqencial. Esse comportamento ser explicado na da u a seo 2.3. ca Apesar das duas formas de uso de threads serem equivalentes, classes que necessitem estender outra classe que no Thread so obrigadas a usar a interface Runnable, j que a a a Java no possui herana mltipla. Alm disso, os puristas em orientao a objetos a c u e ca dizem que normalmente estamos interessados em criar classes que usem threads e no a classes que sejam threads e que, portanto, dever amos implementar toda a lgica em uma o classe que implementa Runnable e depois criar a thread s quando for necessrio. o a
executando ao mesmo tempo. Dessa forma uma thread que est esperando para ser a executada pode estar no estado em execuo e ainda assim estar parada. ca A thread se torna suspensa quando um destes eventos ocorrer: execuo do mtodo sleep(); ca e a thread chama o mtodo wait() para esperar que uma condio seja satisfeita; e ca a thread est bloqueada em uma operao de entrada/sa (I/O). a ca da A chamada ao comando sleep(int seconds) faz com que a thread espere um tempo determinado para executar novamente. A thread no executada durante este intervalo a e de tempo mesmo que o processador se torne dispon vel novamente. Aps o intervalo o dado, a thread volta ao estado em execuo novamente. ca Se a thread chamar o comando wait() ento ela deve esperar uma outra thread avisar a que a condio foi satisfeita atravs dos comandos notify() ou notifyAll(). Esses ca e mtodos sero explicados na seo 3. e a ca Se a thread estiver esperando uma operao de entrada/sa ela retornar ao estado ca da a em execuo assim que a operao for conclu ca ca da. Por m, a thread se torna morta se o mtodo run() chegar ao m de sua execuo e ca ou se uma exceo no for lanada e no for tratada por um bloco try/catch. ca a c a
a thread atual chama yield() ou o mtodo run() termina; e o tempo reservado para a thread termina (s em SOs onde os processos possuem o um tempo mximo para permanecer no processador para serem executados. Disa cutiremos isso na seo 2.3.1). ca Como regra geral, podemos pensar que o escalonador escolher sempre a thread com a maior prioridade para ser executada em determinado momento. Porm, a mquina e a virtual livre para escolher uma thread de menor prioridade para evitar espera indenida e (starvation). 2.3.1 Fatias de tempo Alguns sistemas operacionais como o GNU/Linux ou o Microsoft r WindowsTM 1 fornecem para cada thread fatias de tempo (time-slices), que nada mais so do que o intervalo de a tempo mximo que a thread pode ocupar o processador antes de ser obrigada a ced-lo a e a outra thread. O exemplo da seo 2.1 produziria o seguinte resultado em sistemas que no possuem ca a fatias de tempo para suas threads: Usando Runnable (...) ("Usando Runnable" repetido 998 vezes) Usando Runnable Usando Herana c (...) ("Usando Herana" repetido 998 vezes) c Usando Herana c Ou seja, a primeira thread escalonada seria executada at o m. e J em um ambiente com fatias de tempo, as threads seriam interrompidas e o resultado a da execuo seria mais ou menos assim: ca Usando Usando Usando Usando Usando Usando (...) Runnable Runnable Herana c Runnable Herana c Herana c
A especicao de Java [2] no garante que o ambiente ter fatias de tempo. Fatias ca a a de tempo s ocorrero se o programa rodar em um sistema operacional que possua essa o a caracter stica. Seus programas no devem partir desse pressuposto. a
synchronized public void teste() { faaAlgo(); c } public void teste() { synchronized(this) { faaAlgo(); c } } O lock liberado automaticamente assim que o bloco synchronized termina de ser e executado. Outra caracter stica importante sobre locks em Java que eles so reentrantes. Isso e a quer dizer que uma thread pode readquirir um lock que ela j possuir. Veja o exemplo a abaixo: public class Reentrante { public synchronized void entrar() { reentrar(); } private synchronized void reentrar() { System.out.println("N~o foi necessrio adquirir outro lock"); a a } } Ao executar o mtodo entrar() a thread deve adquir o lock do objeto this. Mas e graas ao fato dos locks serem reentrantes, no foi necessrio readquirir o lock para c a a executar o mtodo reentrar(). O lock s ser liberado quando o mtodo entrar() e o a e terminar se ser executado. Tambm tarefa do synchronized garantir a visibilidade das variveis compartilhae e a das. A mquina virtual de Java mantm uma memria cache para cada uma de suas a e o threads e uma memria principal que acessada por todas as threads. As atualizaes de o e co memria ocorrem primeiro no cache e s depois na memria principal. Para impedir que o o o uma thread atualize dados que so compartilhados apenas em seu cache local, a palavraa chave synchronized obriga que os dados sejam gravados na memria principal. Dessa o forma outras threads sempre acessam o valor mais atual dessas variveis compartilhadas. a
tempo. Segundo, as mudanas feitas em variveis volteis so automaticamente vis c a a a veis para todas as threads. E, por ultimo, indica para o compilador que a varivel pode ser a modicada por outras threads. Veja o seguinte cdigo: o class VolatileTeste { boolean flag; public void foo() { flag = false; if(flag) { (...) } } } Um compilador poderia otimizar o cdigo removendo todo o bloco dentro do if, pois o sabe que a condio do if nunca ser verdadeira. Porm isso no verdade em um ca a e a e ambiente multithreaded. Para impedir esse tipo de otimizao basta declarar a varivel ca a flag como sendo uma volatile boolean flag. Para uma explicao detalhada sobre o verdadeiro signicado das palavras-chave ca volatile e synchronized em termos do modelo de memria do Java, leia o excerto [7] o do livro Concurrent Programming in Java, de Doug Lea.
10
try { // espera at que um produtor coloque um valor. e wait(); } catch (InterruptedException e) { } } cheia = false; // informa ` todos que a caixa foi esvaziada. a notifyAll(); return conteudo; } public synchronized void put(int valor) { while (cheia == true) { try { // espera at que um consumidor esvazie a caixa. e wait(); } catch (InterruptedException e) {} } conteudo = valor; cheia = true; // informa ` todos que um novo valor foi inserido. a notifyAll(); } }
5 Mais Informaes co
Informaes mais detalhadas sobre programao multithreaded podem ser encontradas co ca nas referncias deste texto. e A API [1] da classe Thread de Java traz, alm de seus mtodos e atributos, uma e e pequena explicao sobre o seu funcionamento. A API da classe Object traz explicaes ca co sobre os mtodos wait(), notify() e notifyAll() necessrios para a manipulao de e a ca locks. O cap tulo 17 (Threads and Locks) da especicao da linguagem Java [2] possui inca formaes detalhad co ssimas sobre o funcionamento de threads, locks e sobre a atomicidade de operaes em Java que, apesar de irem muito alm do escopo deste texto, valem a co e leitura. Recomendo tambm a leitura dos artigos [6] e [8], que discutem os problemas da e tcnica de double check, muito utilizada em programao concorrente mas que no e ca a funciona em Java devido a especicao do modelo de memria de Java. ca o
11
Referncias e
[1] JavaTM 2 Plataform API Specication. http://java.sun.com/j2se/1.4.2/docs/ api/. [2] James Gosling, Bill Joy, Gilad Bracha e Guy Steele. The Java Language Specication, 2a edio. Addison-Wesley, 2000. Dispon em http://java.sun.com/docs/books/ ca vel jls/. [3] Mary Campione e Kathy Walrath. The Java Tutorial, terceira edio. Addisonca Wesley, 1998. Dispon em http://java.sun.com/docs/books/tutorial/. vel [4] Brian Goetz. Introduction to Java threads. IBM DeveloperWorks, setembro 2002. Dispon vel em http://www-106.ibm.com/developerworks/edu/ j-dw-javathread-i.html. [5] Alex Roetter. Writing multithreaded Java applications. IBM DeveloperWorks, fevereiro 2001. Dispon vel em http://www-106.ibm.com/developerworks/library/ j-thread.html. [6] Brian Goetz. Double-checked locking: Clever, but broken. JavaWorld, fevereiro 2001. Dispon em http://www.javaworld.com/jw-02-2001/jw-0209-double.html. vel [7] Doug Lea. Synchronization and the Java Memory Model. Dispon vel em http:// gee.cs.oswego.edu/dl/cpj/jmm.html. [8] David Bacon et al. The Double-Checked Locking is BrokenDeclaration. Dispon vel em http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking. html. [9] Neel Kumar. IBM DeveloperWorks, abril 2000. Dispon em http://www-106.ibm. vel com/developerworks/java/library/j-threadsafe/. [10] Gregory Andrews. Foundations of Multithreaded, Parallel, and Distributed Programming. Addison-Wesley, 1999.
12