• Nenhum resultado encontrado

Threads em Java Miguel Jonathan- DCCIM e NCEUFRJ

N/A
N/A
Protected

Academic year: 2018

Share "Threads em Java Miguel Jonathan- DCCIM e NCEUFRJ"

Copied!
12
0
0

Texto

(1)

Threads em Java

Miguel Jonathan- DCC/IM e NCE/UFRJ atualizado em 16/11/2011

Conceitos:

Diferença entre processo e thread:

Um processo é um programa em execução que possui o seu próprio espaço de endereçamento. Um sistema operacional multitarefa é capaz de controlar a execução de mais de um processo ao mesmo tempo, de forma concorrente, fazendo a CPU alternar a execução das instruções de cada processo.

Uma thread é um fluxo sequencial de controle, ou linha de execução, dentro de um processo ou programa. Um processo pode assim ter diversas threads executando concorrentemente, mas todas partilhando o mesmo espaço de endereçamento. Como não há necessidade de trocar de contexto, as threads representam uma forma mais leve de processamento concorrente.

A Máquina Virtual Java (JVM) permite que uma aplicação tenha diversas linhas de execução rodando concorrentemente. Há sempre pelo menos uma thread, que roda o método main(). Essa thread é criada automaticamente, e outras podem ser criadas pelo programador. O programa gc (garbage collector) roda em outra thread também iniciada pela JVM.

Porque usar threads em aplicações:

Threads têm várias utilidades, especialmente quando o uso de um recurso do programa pode demandar muito tempo, e travar outras tarefas que estão aguardando, ou quando se precisa ficar verificando a ocorrência de algum evento para efetuar alguma ação. Nesse último caso, uma thread pode ficar dedicada apenas para verificar periodicamente a ocorrência do evento. A aplicação mais usual é a interação com as interfaces gráficas (menus, botões) quando se quer obter resposta quase que imediata a uma ação.

A classe java.lang.Thread:

Em Java, cada thread é implementada como um objeto, que deve ser uma instância de uma subclasse de java.lang.Thread criada para esse fim.

A interface java.lang.Runnable

Essa interface obriga a ter o método public void run(). Toda thread é criada para executar os comandos de algum método com essa assinatura. A própria classe Thread possui esse método, mas vazio, ou seja, não faz nada.

Como criar uma thread:

Há duas formas básicas para se criar uma thread:

a)

Criar uma subclasse explícita de java.lang.Thread, e redefinir o método run()com os comandos que a thread deverá executar.

Ex:

class MinhaThread extends Thread{ public void run(){

<instruções da thread> }

}

A partir daí é possível criar quantas instâncias se desejar dessa classe. Cada vez que enviar a mensagem start() para uma instância, uma nova linha de execução será iniciada com os comandos do método run(), que rodará em paralelo com as outras threads:

MinhaThread t = new MinhaThread(); t.start();

ou, simplesmente:

(2)

Note que não se pode enviar a mensagem run() diretamente para um objeto Thread. Envia-se a mensagem start(), que criará a thread onde rodarão os comandos do método run().

Obs: Threads podem ser criadas sem referência explícita, como na forma mais simples acima ( ex: new MinhaThread().start();) , sem risco de serem removidas pelo Garbage Collector, pois possuem uma referência interna que permanece até o método run()

associado terminar.

b)

A segunda forma de criar thread é recomendada em situações onde queremos executar um método de uma classe qualquer em uma thread separada, sem que essa classe herde de Thread. Isso é necessário no caso da classe já ser uma subclasse de outra, pois não

poderia herdar também de Thread. Nesse caso, fazemos a classe em questão implementar a interface Runnable e colocamos no método public void run() os comandos que serão executados na thread.

No exemplo abaixo, um método run() da classe X será colocado em uma thread: class X implements Runnable{

public void run(){ <instruções da thread> }

... outros métodos de X, construtores, atributos, etc }

A seguir, em outro método, criamos uma instância de X, e passamos a instância como argumento do construtor da classe Thread:

X x = new X(...);

Thread t = new Thread(x);

O efeito disso é a criação de uma instância de Thread que executará o método run() definido na classe do argumento do construtor.

Enviando a seguir a mensagem start() para a thread fará iniciar uma nova linha de execução com os comandos do método run() da classe X, que rodará em paralelo com as outras threads.

t.start();

Nome da thread:

Toda thread tem um nome (uma string) para poder ser identificada. O nome pode ser passado como um parâmetro do construtor, e pode ser recuperado enviando para a thread a

mensagem getName(). Se um nome não for especificado no construtor, um novo nome será gerado automaticamente para a thread.

Como incluir o nome na chamada do construtor: //subclasse explicita de Thread:

Thread t1 = new MinhaThread("Thread 1");

// usando instancia de classe X que implementa Runnable: Thread t2 = new Thread(new X(), "Thread 2");

Prioridade:

Cada thread possui uma prioridade. Threads com prioridades mais elevadas são executadas preferencialmente antes de threads com menor prioridade. A prioridade default é 5 (em uma escala de 1 a 10), mas pode ser alterada enviando a mensagem setPriority(int) para a thread. (Nota: na classe Thread existem as constantes static MAX_PRIORITY,MIN_PRIORITY e NORM_PRIORITY, com valores 10, 0 e 5, mas que podem variar dependendo da

(3)

Quando existem diversas threads de mesma prioridade sendo executadas, Java aloca um tempo máximo para processar os comandos de cada uma, após o que interrompe a thread em execução e passa a processar os comandos da seguinte.

O método de classe Thread.currentThread():

Esse método permite acessar a thread que está executando no momento em que é chamado. É usado dentro do método run() para permitir identificar e/ou alterar os parâmetros da thread (lembre-se que o run() pode ser de outra classe). Por exemplo, para imprimir o nome da thread onde esse comando está executando:

System.out.println(Thread.currentThread().getName());

Fazendo uma thread “dormir” por um certo tempo:

O método de classe Thread.sleep(long milisseg)permite interromper a execução da thread pelo tempo dado no argumento.

Deve ser usado dentro de um bloco try porque, caso a thread receba uma mensagem interrupt() enquanto estiver parada por um sleep(), será lançada uma

InterruptedException.

Cedendo o tempo de processamento para outras threads:

Uma thread pode ceder o restante do tempo de processamento alocado a ela, executando o comando yield(). O controle passa para a próxima thread que estiver aguardando. Evita desperdiçar tempo de processamento.

Exemplos:

Os exemplos abaixo simulam uma corrida entre dois times, Flamengo e Botafogo.

Nestes dois exemplos, duas threads escrevem “em paralelo” na console os números de 1 a 99, seguido do nome do time.

1. No primeiro exemplo, uma subclasse de Thread é definida, onde o seu método run() simula a corrida. Duas instâncias dessa classe são criadas para representar os dois jogadores. Cada thread recebe o nome de um time (Flamengo e Botafogo) passado pelo construtor. Para tornar o resultado aleatório, e possibilitar uma melhor observação dos resultados, cada thread “dorme” uma quantidade aleatória de milisegundos entre 0 e 299 antes de imprimir a próxima linha. Para isso é usado o método estático sleep(long miliseg) da classe Thread.

Antes de terminar, cada thread imprime a palavra TERMINOU, seguido do seu nome. O método getName() da classe Thread retorna a string com o nome.

public class ThreadSimples extends Thread { public ThreadSimples(String str) { super(str);

}

public void run() {

for (int i = 0; i < 100; i++) {

System.out.println(i + " " + getName()); try {

sleep((long)(Math.random() * 300)); }

catch (InterruptedException e) {} }

System.out.println("TERMINOU " + getName()+"!"); }

}

public class TesteDuasThreads {

public static void main (String[] args) {

ThreadSimples bota = new ThreadSimples("BOTAFOGO"); bota.setPriority(4);

ThreadSimples mengo =new ThreadSimples("FLAMENGO"); mengo.setPriority(6);

(4)

bota.start();

System.out.println("Main terminado!"); }

}

2. O segundo exemplo é essencialmente o mesmo que o primeiro, só que nesse caso não se cria nenhuma subclasse de Thread.

Em vez disso uma classe Time é definida, que implementa a interface Runnable. O método run() de Time contém os mesmos comandos que o método run() da subclasse de Thread do primeiro exemplo. Nesse caso, a prioridade de cada thread é passada pelo construtor do time.

public class Time implements Runnable { String nome;

int priority;

public Time(String nome, int priority) { this.nome=nome;

this.priority=priority; }

public void run() {

Thread.currentThread().setPriority(priority); for (int i = 0; i < 100; i++) {

System.out.println(i + "R " + nome); try {

Thread.sleep((long)(Math.random() * 300)); }

catch (InterruptedException e) {} }

System.out.println("TERMINOU " + nome+"!"); }

}

public class TesteDuasThreads {

public static void main (String[] args) {

Time bota = new Time("BOTAFOGO",1); Time mengo =new Time("FLAMENGO",7);

new Thread(mengo).start(); new Thread(bota).start();

System.out.println("Main terminado!"); }

}

Interrupção de uma Thread

Toda thread tem uma variável interna, um flag binário, que contém o seu "estado de interrupção" (interrupt status).

Esse flag fica "setado", ou seja, valendo 1, se a thread recebeu uma solicitação para ser interrompida, por meio da mensagem interrupt().

Essa mensagem não para a thread automaticamente. A própria thread deve avaliar o que fazer e como se terminar.

O caso típico é a thread fazer alguma finalização e executar return, para se terminar.

Caso a thread esteja bloqueada temporariamente por ter executado um sleep(), join() ou wait(), ela receberá também uma InterruptedException ao receber a mensagem interrupt(). Nesse caso, será obrigatório haver blocos try-catch para essa exceção verificada, e o bloco catch poderá fazer o tratamento da interrupção. É ο caso do primeiro exemplo mostrado a seguir.

(5)

thread que está executando (a current thread), retornando true se o status for 1, e false em caso contrário.

A execução desse método também “resseta” automaticamente o status de interrupção para zero.

Exemplos:

As classes abaixo mostram o uso de interrupção nos dois casos. Uma thread separada da thread main() fica imprimindo indefinidamente na console os múltiplos de um valor raiz. A impressão deve ser interrompida quando a tecla ENTER do teclado for acionada dentro do método main.

As classes devem ser usadas aos pares. O método main() dispara a thread e fica aguardando a entrada do teclado, com o comando br.readLine(). Em seguida, envia para a thread a mensagem interrupt().

No primeiro caso, a thread usa o método Thread.sleep(1) para "dormir" 1 milissegundo entre cada impressão. O bloco catch pega a interrupção e termina a thread com return.

No segundo caso, não é usado o sleep(), e por isso não existem blocos try-catch para detectar a interrupção. O método Thread.interrupted() é usado para checar, a cada volta do laço de impressão, se o status de interrupção mudou para 1.

a) Interrupção de thread por meio de InterruptedException (thread tem sleep()):

public class ImprimeThreadComSleep extends Thread{

private int raiz;

public ImprimeThreadComSleep(int raiz){

this.raiz = raiz; }

public void run(){

int i=1; int n=raiz;

while(true){

System.out.println(n= raiz*i); i++;

try{

sleep(1); }

catch(InterruptedException e){

System.out.println ("Thread interrompida enquanto dormia!"); return; // termina a thread

} } } }

import java.io.*;

public class InterrompeThread1 {

public static void main(String[]args){

ImprimeThreadComSleep t;

if (args.length < 1){

System.out.println("Este programa necessita de um argumento inteiro, a raiz");

return;

} else {

try{

int raiz = Integer.parseInt(args[0]); t = new ImprimeThreadComSleep(raiz); t.start();

}catch(NumberFormatException nf){

System.out.println("Este programa necessita de um argumento inteiro"); return;

(6)

}

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

try{

br.readLine(); // aguarda digitar ENTER

}

catch (IOException e) {}

t.interrupt(); } // main termina

}

b) Interrupção de thread por meio de Thread.interrupted() (thread sem sleep()): public class ImprimeThreadSemSleep extends Thread {

private int raiz;

public ImprimeThreadSemSleep(int raiz) {

this.raiz = raiz;

}

public void run() { int i = 1, n = raiz;

while (true) {

System.out.println(n = raiz * i); i++;

if (Thread.interrupted()) {

System.out.println("Thread interrompida sem estar dormindo!"); return;

} } } }

import java.io.*;

public class InterrompeThread2 {

public static void main(String[]args){

ImprimeThreadSemSleep t;

if (args.length < 1){

System.out.println("Este programa necessita de um argumento inteiro, a raiz"); return;

}

else {

try{

int raiz = Integer.parseInt(args[0]);

t = new ImprimeThreadSemSleep(raiz); t.start();

}

catch(NumberFormatException nf){

System.out.println("Este programa necessita de um argumento inteiro"); return;

} }

BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); try{

br.readLine(); // aguarda digitar ENTER

}

catch (IOException e) {} t.interrupt();

}// main termina

}

Uso Concorrente de Recursos

(7)

conseguido em Java marcando, com uma sinalização de "tranca" ou "lock", a parte de código em que a thread faz uso de um recurso partilhado com outras threads.

O atendimento às threads que disputam um mesmo recurso não é feito exatamente na forma de uma fila de espera, pois o escalonador da JVM não é muito determinístico, embora possa seguir sugestões dadas por comandos como yield() e setPriority(). Java usa o mecanismo de semáforos.

Semáforo (ou monitor, lock/tranca) e a palavra-chave synchronized.

Um semáforo é um sinalizador associado a um recurso que é monitorado para ser usado por uma única thread de cada vez. O recurso é sempre associado a um objeto. Todo objeto em Java contém uma única "tranca", que é partilhada por todos os métodos sincronizados desse objeto.. Se o valor do semáforo for zero, o objeto está disponível.

Em Java, esse mecanismo é implementado com o uso da palavra-chave synchronized para prefixar um método que deve rodar em uma thread com uso exclusivo do objeto que ativou o método. Uma thread, ao entrar em um método sincronizado de um objeto, toma posse e incrementa o semáforo do objeto, impedindo outras threads de entrarem em outros métodos sincronizados da mesma classe, até a thread que detém a tranca terminar de executar o método.

Ao sair de um método sincronizado, a thread decrementa o semáforo do objeto, que ficará liberado para uso por outras threads sincronizadas, quando o valor do semáforo voltar a zero (uma mesma thread, após adquirir o semáforo de um objeto, pode entrar em vários métodos sincronizados do mesmo objeto, e cada vez que isso ocorre o semáforo será incrementado mais uma vez).

Quando uma thread executa um método sincronizado m(), que foi ativado a partir de um determinado objeto x, como em x.m(), o acesso ao objeto x fica bloqueado a todas as demais threads que tentarem executar esse ou outros métodos sincronizados da classe de x , até que o método m() termine e libere a tranca.

Podemos também restringir o acesso a apenas um bloco de código dentro de um método, prefixando o bloco com a palavra synchronized, com um objeto entre parênteses:

Ex: synchronized(x) { <bloco de código> }

Essa construção obriga a que a tranca do objeto x deva ser adquirida por uma thread para executar o bloco, e enquanto a tranca não for liberada por essa thread, nenhuma outra poderá executar esse bloco para esse objeto.

Trancas de Classes:

Classes também são objetos, e possuem trancas. A tranca de uma classe se refere aos seus métodos estáticos sincronizados (synchronized static), e quando uma thread executa um método estático sincronizado, o acesso às variáveis de classe fica bloqueado para outras threads.

Liberando a tranca do objeto com o wait():

De dentro de um método ou bloco sincronizado, o envio da mensagem wait()ao objeto que ativou o método (this) suspende a execução da thread, e libera a tranca desse objeto.

(8)

em espera acorda e retoma ao estado "runnable", passando a poder receber tempo de processamento. Tipicamente, essa thread aguarda que uma condição externa mude (em geral por conta da ação de outras threads).

Exemplo de uso de threads com sincronismo

(Adaptado de um exemplo de Paulo César M. Jeveaux em www.portaljava.com.br)

Nesse exemplo, um banco tem 10 contas.

Cada conta é criada com um valor inicial de 10.000 reais.

São então criadas 10 threads, uma para cada conta, e as 10 threads passam a executar seus métodos run() em paralelo, com igual prioridade.

Cada thread fica continuamente transferindo uma quantia aleatória (menor que 5000) para uma outra das 9 contas, escolhida também aleatoriamente. Como não haverá depósitos ou saques externos, o total do dinheiro no banco (a soma dos saldos das 10 contas) deveria ficar constante em 100.000 reais.

Para fazer uma transferência, cada thread executa duas operações em sequência, em um método transfere():

a) tenta debitar da sua conta (se houver saldo) uma quantia de dinheiro escolhida aleatoriamente, até 5000 reais; e

b) após conseguir debitar, credita a mesma quantia em outra conta escolhida também aleatoriamente.

As duas ações conjuntas, débito de uma conta, e crédito na outra conta, formam o que se chama uma transação atômica, no sentido de que deve ser indivisível. Ou seja, ela não deve ser interrompida no meio, antes que outras ações manipulem (leiam ou alterem) os valores das contas, ou resultados estranhos podem ocorrer, como veremos a seguir.

Cada thread entra num laço em que testa se a conta tem saldo suficiente para debitar a quantia a transferir e, não tendo, “dorme” por 5 milisegundos antes de testar novamente. A ideia é que, em algum momento, depósitos terão sido feitos aleatoriamente que levem o saldo a ser suficiente para realizar a transferência.

Esse exemplo tem duas versões:

1) Na primeira versão, sem sincronização, não há garantia que o método transfere() completará as duas transferências antes que outra thread possa acessar a área de memória das contas do banco:

Nesta versão, as 10 threads competem por ciclos de CPU sem qualquer restrição para acessar os valores das contas. Ocorre que a máquina virtual Java (JVM) pode a qualquer momento interromper a execução de uma thread e passar o controle para outra, estando essa execução em qualquer instrução (basta ter terminado a execução de um bytecode).

Nessa versão, pode ocorrer que uma ou mais transações não tenham terminado (digamos, só foi feito o débito, mas ainda não foi feito o crédito subsequente) antes de passar o controle para outra thread que calcula a soma dos valores das contas. Evidentemente que, nesse caso, a soma poderá dar um resultado errado, pois deveria ser feita somente depois que todos os créditos forem realizados.

(9)

método sincronizado, a partir da chamada por um objeto, como em b.transfere(), a thread passa a “possuir o monitor” (a tranca, ou lock) do objeto que ativou o método, no caso o banco b. Isso significa que somente essa thread pode acessar variáveis de instância ou de classe desse objeto, até que termine, ou libere voluntariamente o monitor com uma chamada de wait().

Dentro de um método sincronizado, caso ocorra a necessidade de aguardar algum tempo para iniciar a transação atômica, e para não manter as demais threads esperando, é possível enviar o comando wait() para o objeto que ativou esse método. O efeito é bloquear essa thread e liberar o monitor ( i.e., a tranca) para que o escalonador de tarefas escolha outra thread para assumi-lo. A thread ficará bloqueada até que o objeto receba a mensagem notify() ou notifyAll(), que desbloqueia as threads em estado de bloqueio por wait.

A diferença entre os dois casos é que notify() só desbloqueia uma das threads que estava em wait, que é escolhida arbitrariamente, enquanto que notifyAll() desbloqueia todas. Depois de desbloqueada, a thread passa a competir com as demais que estão ativas pela posse do monitor, e só depois de recebê-la é que voltará a executar.

Observações:

a) Os métodos wait(), notify(), e notifyAll() só podem ser chamados de dentro de um bloco sincronizado. Se isso não for feito, o compilador não reclama, mas em tempo de execução ocorrerá uma exceção do tipo IllegalMonitorStateException.

b) As chamadas de wait() devem ser feitas sempre dentro de um laço que testa a condição para permanecer em wait, e devem estar dentro de um bloco try-catch para capturar uma InterruptedException. Essa exceção pode ocorrer se a thread em questão receber uma mensagem interrupt() enquanto estiver em wait, aguardando um notify().

c) Os métodos wait(), notify()e notifyAll() são definidos na classe Object. Ou seja, todo e qualquer objeto pode ser sincronizado e ter um monitor associado, e receber essas mensagens.

Implementação do exemplo:

Primeiro caso: sem usar sincronização: public class Banco {

public static final int VALOR_TOTAL = 10000; // total de cada conta

public static final int NUM_CONTAS = 10; // número de contas no banco private long conta[]; // array que armazena os valores de cada conta private int transferencias; // número de transferências bancárias

public Banco() {

conta = new long[NUM_CONTAS];

for(int i = 0; i < NUM_CONTAS; i++) { conta[i] = VALOR_TOTAL;

}

transferencias = 0; teste();

}

public void transfere(int de, int para, int quantia) { while(conta[de] < quantia) {

try {

Thread.sleep(5);

(10)

}

conta[de] -= quantia;

try {

Thread.sleep(1); // para aumentar a chance da thread parar no meio }catch(InterruptedException e) {}

conta[para] += quantia; transferencias++;

if(transferencias % 5000 == 0) teste(); }

public void teste() { long soma = 0;

for(int i = 0; i < NUM_CONTAS; i++) soma = soma + conta[i];

System.out.println("No.de transações: " + transferencias + " Soma: " + soma);

}

} // fim da classe Banco

public class BancoSemSincronismo {

public static void main(String[] args) { Banco b = new Banco();

for(int i=0; i < Banco.NUM_CONTAS; i++) { new Transferencia(b, i).start();

} } }

public class Transferencia extends Thread { private Banco b;

private int de;

public Transferencia(Banco b, int de) { this.b = b;

this.de = de; }

public void run() { int para;

while(true) { do{

para = (int)(Banco.NUM_CONTAS * Math.random()); } while(para==de);

int quantia = 1+(int)(Banco.VALOR_TOTAL * Math.random()) /2; b.transfere(de, para, quantia);

try {

sleep(1); }

catch(InterruptedException e) {} }

} }

(11)

class Banco{

public static final int VALOR_TOTAL = 10000; //Total de cada conta public static final int NUM_CONTAS = 10; //Número de contas no banco private long conta[]; //Array que armazena o valor das contas

private int transferencias; //indica o número de transferências bancárias

public Banco() {

conta = new long[NUM_CONTAS];

for(int i = 0; i < NUM_CONTAS; i++) { conta[i] = VALOR_TOTAL;

}

transferencias = 0; teste();

}

public synchronized void transfere(int de, int para, int quantia) {

while(conta[de] < quantia) { try {

wait();

}catch(InterruptedException e) {} }

conta[de] -= quantia; try {

Thread.sleep(1); // para aumentar a chance da thread parar no meio }catch(InterruptedException e) {}

conta[para] += quantia; transferencias++;

if(transferencias % 3000 == 0) teste(); notify();

}

public void teste() { long soma = 0;

for(int i = 0; i < NUM_CONTAS; i++) soma += conta[i];

System.out.println("Transações: " + transferencias + " Soma: " + soma); }

} // fim da classe Banco

public class BancoComSincronismo {

public static void main(String[] args) { Banco b = new Banco();

for(int i=0; i < Banco.NUM_CONTAS; i++) { new Transferencia(b, i).start();

} } }

public class Transferencia extends Thread { private Banco b;

private int de;

(12)

this.de = de; }

public void run() { int para;

while(true) { do{

para = (int)(Banco.NUM_CONTAS * Math.random()); }while(para==de);

int quantia = 1+(int)(Banco.VALOR_TOTAL * Math.random()) /2; b.transfere(de, para, quantia);

try {

sleep(1); }

catch(InterruptedException e) {} }

} }

Referências:

Bruce Eckel, “Thinking in Java”, 3rd Edition, disponível em:

http://www.ibiblio.org/pub/docs/books/eckel/TIJ-3rd-edition4.0.zip

Sun Java Tutorial – parte dedicada a Threads e processos concorrentes:

Referências

Documentos relacionados

3 - Quando não possa reparar-se convenientemente o caixão deteriorado, encerrar-se-à o mesmo noutro caixão de zinco ou será removido para sepultura à escolha

O PROGRAMA DE PÓS-GRADUAÇÃO EM FISIOPATOLOGIA E CIÊNCIAS CIRÚRGICAS, DA UNIVERSIDADE DO ESTADO DO RIO DE JANEIRO - UERJ torna público o presente Edital,

Símbolos para regulação e orientação de procedimentos de segurança e higiene; Normas e órgãos de inspeção sanitária (ANVISA, Ministério da Agricultura e da Saúde,

Tuba Timbal Caja Platos+

Essas operações, com e sem risco de crédito, são contabilizadas em conta do ativo “Outros créditos de operações com planos de assistência à saúde”

de burlar as disposições do presente Regulamento e suas regras, o consumidor perderá o direito aos dotz, sem prejuízo de qualquer outra iniciativa por parte

Para instalar o porteiro AM-PPR com apenas uma tecla reservada para central AM-C100 é necessário colocar o “JUMPER” J1 e retirar o “JUMPER” J2 existente na placa

Demonstrado que o bloqueio atingiu a conta-salário e aplicações em poupança, concede-se em parte a segurança para determinar o desbloqueio da conta-salário da