Linguagem de Programação Flashcards
Vantagens das coleções genéricas
Exemplo:
List<int> numeros = new List<int>();
numeros.Add(10);
numeros.Add("Texto"); // Erro de compilação!
- Evita erros de casting (conversão de tipo) em tempo de execução.
- Como a lista foi declarada como
List<int>
, tentar adicionar uma string gera erro na compilação.
- Como a lista foi declarada como
- Melhor legibilidade e manutenção do código
- O uso de tipos explícitos torna o código mais compreensível, pois deixa claro que tipo de dado a coleção deve conter.
- Eliminação da necessidade de conversões explícitas
- Nas coleções não genéricas, frequentemente é necessário fazer casting ao recuperar um item, o que pode levar a exceções em tempo de execução.
- Melhor desempenho
- Como não há conversões de tipo em tempo de execução, o uso de coleções genéricas melhora a eficiência do código.
O que são tipos genéricos?
Tipos genéricos são um recurso da programação orientada a objetos que permite criar classes, interfaces e métodos que podem operar com diferentes tipos de dados sem perder segurança de tipo. Eles são amplamente utilizados para aumentar a reutilização de código e evitar a necessidade de conversões explícitas.
Exemplo:
// Definição de uma classe genérica com um parâmetro de tipo T
public class Caixa<T> {
private T objeto;
public void setObjeto(T objeto) {
this.objeto = objeto;
}
public T getObjeto() {
return objeto;
}
}
// Uso da classe genérica
public class Main {
public static void main(String[] args) {
Caixa<String> caixaDeTexto = new Caixa<>();
caixaDeTexto.setObjeto("Olá, Genéricos!");
System.out.println(caixaDeTexto.getObjeto()); // Saída: Olá, Genéricos!
Caixa<Integer> caixaDeNumeros = new Caixa<>();
caixaDeNumeros.setObjeto(42);
System.out.println(caixaDeNumeros.getObjeto()); // Saída: 42
}
}
Exemplo de coleção não genérica
List<String>
lista = new ArrayList<>();
lista.add(“Texto”);
String texto = lista.get(0); // Sem necessidade de casting
Como funciona o Garbage Collector?
O Garbage Collector segue um processo básico:
- Alocação de memória → Quando um objeto é criado, ele é armazenado na memória heap gerenciado. Não é o heap nativo/principal do sistema operacional.
- Verificação de referências → O GC identifica quais objetos ainda estão sendo usados pelo programa.
- Coleta de lixo → Objetos sem referências são removidos da memória para liberar espaço.
Quando o Garbage Collector é acionado?
O GC pode ser acionado automaticamente pelo runtime da linguagem quando a memória disponível está baixa ou quando o sistema decide que é um bom momento para otimizar a memória. Em algumas linguagens, o GC pode ser chamado manualmente, mas isso não é recomendado na maioria dos casos.
Como funciona o profilling?
-
Instrumentação
- O código é modificado para incluir medições de tempo de execução.
- Exemplo: Adicionar
System.nanoTime()
em Java para medir tempos de execução.
-
Amostragem (Sampling)
- O profiler monitora periodicamente quais funções estão sendo executadas, sem modificar o código.
- Menos impacto no desempenho, mas pode perder detalhes de execuções rápidas.
-
Tracing (Rastreamento)
- Registra cada chamada de função e evento do programa, capturando informações detalhadas.
- Pode gerar grandes volumes de dados e impactar a performance.
-
Heap & Memory Profiling
- Analisa uso de memória e possíveis vazamentos de memória.
- Mede alocações e desalocações de objetos.
O que é profilling?
Profiling é uma técnica usada para analisar o desempenho de um programa, identificando gargalos e otimizando o uso de recursos, como CPU, memória e operações de entrada/saída (I/O).
Quais os problemas mais comuns em Threads?
-
Race Condition (Condição de Corrida) – Ocorre quando múltiplas threads tentam modificar um recurso compartilhado ao mesmo tempo, levando a resultados imprevisíveis.
- Solução: Usar mecanismos de sincronização como locks (travas).
-
Deadlock – Acontece quando duas ou mais threads ficam presas esperando uma pela outra.
- Solução: Evitar dependências circulares entre threads.
-
Starvation (Inanição) – Quando uma thread não consegue acesso a um recurso porque outras estão monopolizando.
- Solução: Implementar estratégias de escalonamento justas.
O que é uma Thread?
Uma thread é uma unidade básica de execução dentro de um processo. Em termos simples, é como se fosse uma “tarefa” que pode rodar de forma independente dentro de um programa. Em linguagens de programação, threads são usadas para executar múltiplas operações ao mesmo tempo (paralelismo ou concorrência), permitindo que um programa realize várias tarefas simultaneamente.
Threads x Processos
- Processo: É um programa em execução que possui sua própria memória e recursos.
- Thread: É uma subdivisão de um processo, compartilhando a mesma memória e recursos com outras threads dentro do mesmo processo.
Um processo pode ter múltiplas threads, todas compartilhando o mesmo espaço de memória, o que permite comunicação rápida entre elas.
Quais os tipos de Threads?
- Threads de Usuário – Criadas e gerenciadas pelo próprio programa, sem intervenção direta do sistema operacional.
- Threads de Kernel – Controladas pelo sistema operacional, podem ser distribuídas entre múltiplos núcleos da CPU.
O que é mutex?
Mutex (Mutual Exclusion, ou Exclusão Mútua) é um mecanismo de sincronização de threads que garante que apenas uma thread possa acessar um recurso compartilhado por vez.
Ele resolve problemas de race conditions (condições de corrida), onde múltiplas threads tentam modificar um mesmo recurso simultaneamente, causando comportamentos imprevisíveis.
Como Funciona o Mutex?
1. Uma thread bloqueia (lock) o mutex antes de acessar um recurso compartilhado.
2. Enquanto a thread está usando o recurso, outras threads ficam bloqueadas aguardando a liberação.
3. Quando a thread termina de usar o recurso, ela desbloqueia (unlock) o mutex.
4. A próxima thread na fila pode então acessar o recurso.
O que é escalonamento?
Escalonamento (ou scheduling) é o processo de gerenciar a execução de múltiplas threads ou processos em um sistema computacional, garantindo que todos tenham tempo suficiente de CPU para rodar de maneira eficiente.
Em sistemas monotarefa, apenas um processo é executado por vez. Já em sistemas multitarefa, o escalonador decide qual processo ou thread será executado a cada instante, melhorando o uso dos recursos do sistema.
O que é um semaphores?
Um semáforo mantém um contador que representa quantas threads podem acessar um recurso ao mesmo tempo.
Regras básicas:
1. Uma thread precisa “pegar” (decrementar) o semáforo antes de acessar o recurso.
2. Quando termina, a thread libera (incrementa) o semáforo, permitindo que outra thread acesse o recurso.
3. Se o contador chegar a zero, novas threads precisam esperar até que alguma outra libere o semáforo.
Tipos de Semáforo
Semáforo Binário (Mutex) – Funciona como um cadeado, permitindo apenas uma thread por vez no recurso (contador = 1).
Semáforo Contador (Counting Semaphore) – Permite que até N threads acessem um recurso ao mesmo tempo (contador = N).