🖥️ ¿Qué es un proceso?
Un proceso es la ejecución de un programa. Cuando se lanza una aplicación —ya sea un navegador web, un editor de texto o un programa Java—, el sistema operativo carga las instrucciones y los datos del programa en la memoria principal (RAM) y crea un proceso. A partir de ese momento, el procesador (CPU) se encarga de ir ejecutando las instrucciones una a una.
Es importante distinguir entre programa y proceso. Un programa es un archivo estático que reside en el disco duro: un conjunto de instrucciones escritas en un lenguaje de programación. El proceso, en cambio, es la entidad dinámica que nace cuando el sistema operativo carga ese programa en memoria y comienza a ejecutarlo. Un mismo programa puede dar lugar a múltiples procesos simultáneos: por ejemplo, si se abren dos ventanas del navegador, cada una es un proceso independiente del mismo programa.
🔄 Estados de un proceso
Un proceso no está ejecutándose todo el tiempo. A lo largo de su vida, pasa por distintos estados gestionados por el sistema operativo. Comprender estos estados es fundamental para entender cómo funciona la multitarea en los ordenadores modernos.
| Estado | Descripción | Ejemplo |
|---|---|---|
| Preparado | El proceso está listo para ejecutarse, solo le falta que la CPU le dé su turno | Un programa Java recién lanzado, esperando su turno de CPU |
| En ejecución | El proceso tiene la CPU asignada y sus instrucciones se están ejecutando | Un bucle for que está recorriendo un array |
| Bloqueado | El proceso espera un evento externo antes de poder continuar | Un programa que espera que el usuario pulse una tecla (Scanner.nextLine()) |
🔹 Ciclo de vida completo
Las transiciones entre estados siguen un flujo bien definido:
// Transiciones de estado de un proceso
[CREACIÓN] ──→ [PREPARADO] ──→ [EN EJECUCIÓN] ──→ [FINALIZADO]
↑ │
│ ↓
└──── [BLOQUEADO] ←┘
Transición 1 (Creación → Preparado): El sistema operativo crea el proceso y lo coloca en la cola de procesos listos. Transición 2 (Preparado → En ejecución): El planificador del SO asigna la CPU al proceso. Transición 3 (En ejecución → Bloqueado): El proceso necesita esperar un evento externo (lectura de disco, entrada del usuario). Transición 4 (Bloqueado → Preparado): El evento esperado ocurre y el proceso vuelve a estar listo. Transición 5 (En ejecución → Preparado): El SO retira la CPU al proceso para dársela a otro (cambio de contexto). Transición 6 (En ejecución → Finalizado): El proceso completa todas sus instrucciones.
java MiPrograma, la JVM crea un proceso que pasa por todos estos estados. Si el programa llama a Thread.sleep(1000), el hilo queda bloqueado durante un segundo antes de volver al estado preparado.🔹 ¿Cómo se ve un proceso en Java?
Cada vez que se ejecuta un programa con java MiClase, se crea un proceso del sistema operativo que contiene la JVM. Dentro de ese proceso, la JVM gestiona uno o más hilos (threads). El hilo principal es el que ejecuta el método main.
public class DemoProceso { public static void main(String[] args) { // Obtener información del proceso actual long pid = ProcessHandle.current().pid(); System.out.println("PID del proceso: " + pid); // Número de procesadores disponibles int cpus = Runtime.getRuntime().availableProcessors(); System.out.println("CPUs disponibles: " + cpus); // Memoria asignada al proceso long memoriaTotal = Runtime.getRuntime().totalMemory() / 1024 / 1024; System.out.println("Memoria asignada: " + memoriaTotal + " MB"); // Nombre del hilo actual String hilo = Thread.currentThread().getName(); System.out.println("Hilo actual: " + hilo); } }
$ java DemoProceso
PID del proceso: 12847
CPUs disponibles: 8
Memoria asignada: 256 MB
Hilo actual: main
🔧 Clasificación por hardware: mono y multiprocesador
Desde el punto de vista del hardware, los sistemas informáticos se clasifican según el número de procesadores (CPUs) de los que disponen. Esta clasificación es esencial para entender por qué algunos programas pueden ejecutar tareas en paralelo y otros no.
| Tipo de sistema | Descripción | Ejemplo actual |
|---|---|---|
| Monoprocesador | Una sola CPU. Solo puede ejecutar una instrucción a la vez | Microcontroladores Arduino, ordenadores antiguos de los años 90 |
| Multiprocesador fuertemente acoplado | Varias CPUs que comparten la misma memoria RAM | Cualquier PC o portátil moderno con procesador multinúcleo (4, 8, 16 cores) |
| Multiprocesador débilmente acoplado | Varias CPUs con memorias independientes, conectadas por red | Clústeres de servidores, redes de ordenadores, supercomputadores |
🔹 Paralelismo en Java
Java proporciona herramientas para aprovechar los sistemas multiprocesador. La clase Thread permite crear hilos de ejecución que el sistema operativo puede asignar a diferentes núcleos de la CPU:
public class DemoParalelismo { public static void main(String[] args) { int nucleos = Runtime.getRuntime().availableProcessors(); System.out.println("Este equipo tiene " + nucleos + " núcleos."); // Crear dos hilos que se ejecutan en paralelo Thread hilo1 = new Thread(() -> { for (int i = 1; i <= 3; i++) System.out.println("Hilo A - iteración " + i); }); Thread hilo2 = new Thread(() -> { for (int i = 1; i <= 3; i++) System.out.println("Hilo B - iteración " + i); }); hilo1.start(); hilo2.start(); } }
$ java DemoParalelismo
Este equipo tiene 8 núcleos.
Hilo A - iteración 1
Hilo B - iteración 1
Hilo A - iteración 2
Hilo B - iteración 2
Hilo A - iteración 3
Hilo B - iteración 3
💾 Clasificación por software: multiprogramación y multiprocesamiento
Desde el punto de vista del software, los sistemas se clasifican según cómo gestionan la ejecución de los procesos. Esta distinción es clave para entender cómo un ordenador con una sola CPU puede aparentar que ejecuta múltiples programas a la vez.
| Tipo | Descripción | ¿Paralelismo real? |
|---|---|---|
| Multiprogramación | Varios procesos comparten una sola CPU, intercalando su ejecución | No — concurrencia simulada |
| Multiprocesamiento | Cada proceso se ejecuta en su propia CPU | Sí — paralelismo auténtico |
La multiprogramación fue la primera técnica que permitió a los sistemas operativos gestionar múltiples programas. Cuando un proceso se bloquea esperando una operación de entrada/salida, la CPU pasa a ejecutar otro proceso preparado. Así se maximiza el uso del procesador.
El multiprocesamiento va un paso más allá: cada proceso dispone de su propia CPU. Los ordenadores actuales combinan ambas técnicas: multiprocesamiento real entre núcleos y multiprogramación dentro de cada núcleo.
🧭 Paradigmas de programación
Un paradigma de programación es una forma de pensar y estructurar el código. No se trata solo de sintaxis, sino de la filosofía que subyace al diseño del programa: ¿cómo se organizan los datos?, ¿cómo se describe la solución?, ¿en qué se centra el programador?
Los paradigmas principales son tres: imperativo, orientado a objetos y declarativo. Un lenguaje puede soportar más de uno. Java es fundamentalmente orientado a objetos pero incorpora elementos de programación funcional desde Java 8.
| Paradigma | Enfoque central | Lenguajes representativos |
|---|---|---|
| Imperativo | Secuencia de instrucciones que modifican el estado | C, Pascal, BASIC, Fortran |
| Orientado a objetos | Objetos que encapsulan datos y comportamiento | Java, C#, Python, C++ |
| Declarativo | Describe qué se quiere obtener, no cómo | SQL, Prolog, Haskell, HTML/CSS |
📋 Programación imperativa
La programación imperativa es el paradigma más antiguo e intuitivo. El programador escribe una secuencia de instrucciones paso a paso que el ordenador ejecuta en orden. El foco está en cómo se transforman los datos.
Las estructuras fundamentales son las variables, los condicionales (if/else), los bucles (for, while) y las funciones.
// Ejemplo imperativo en Java: suma de los primeros N números public class SumaImperativa { public static void main(String[] args) { int n = 10; int suma = 0; for (int i = 1; i <= n; i++) { suma += i; } System.out.println("La suma de 1 a " + n + " es: " + suma); } }
$ java SumaImperativa
La suma de 1 a 10 es: 55
Dentro de la programación imperativa existen variantes como la programación estructurada (que evita goto y organiza el código en bloques) y la programación concurrente (que gestiona múltiples hilos de ejecución).
📦 Programación orientada a objetos
La programación orientada a objetos (POO) supuso un cambio de perspectiva fundamental. En lugar de centrarse en los algoritmos, la POO gira en torno al concepto de objeto: una entidad que agrupa datos (atributos) y operaciones (métodos) en una misma unidad.
Este enfoque se asemeja a la forma en que el ser humano percibe el mundo real. Un objeto «Pelota» tiene características (color, tamaño, peso) y acciones (botar, lanzar, inflar). Otras operaciones no son válidas: no se puede «beber» una pelota. La POO captura esta intuición y la traslada al código.
// Ejemplo de POO en Java: una pelota con atributos y métodos public class Pelota { private String color; private double diametro; private boolean inflada; public Pelota(String color, double diametro) { this.color = color; this.diametro = diametro; this.inflada = true; } public void botar() { if (inflada) { System.out.println("¡La pelota " + color + " bota!"); } else { System.out.println("La pelota está desinflada, no puede botar."); } } public void desinflar() { inflada = false; System.out.println("Pelota desinflada."); } public String toString() { return "Pelota[" + color + ", " + diametro + " cm, " + (inflada ? "inflada" : "desinflada") + "]"; } }
Los cuatro pilares fundamentales de la POO son la abstracción (simplificar la realidad modelando solo lo relevante), el encapsulamiento (ocultar los detalles internos), la herencia (crear jerarquías de clases) y el polimorfismo (permitir que un mismo método se comporte diferente según el objeto).
public static void main) es un método de una clase. Esto contrasta con lenguajes como C o Python, donde se puede escribir código fuera de cualquier clase.🎯 Programación declarativa
La programación declarativa adopta un enfoque radicalmente diferente: el programador describe qué resultado quiere obtener, sin especificar paso a paso cómo conseguirlo. El motor del lenguaje se encarga de encontrar la forma óptima.
Se divide en dos grandes ramas: la programación lógica (como Prolog, basada en reglas y hechos) y la programación funcional (como Haskell, basada en funciones puras sin efectos secundarios).
// Comparación: filtrar números pares de una lista // Enfoque IMPERATIVO: describimos CÓMO hacerlo paso a paso List<Integer> paresImperativo = new ArrayList<>(); for (int num : numeros) { if (num % 2 == 0) { paresImperativo.add(num); } } // Enfoque DECLARATIVO (funcional): describimos QUÉ queremos List<Integer> paresDeclarativo = numeros.stream() .filter(num -> num % 2 == 0) .collect(Collectors.toList());
El ejemplo más conocido de lenguaje declarativo es SQL: al escribir SELECT nombre FROM empleados WHERE salario > 3000, se describe qué datos se necesitan, no cómo recorrer las tablas.
🖥️ Entornos de ejecución: consola vs. gráfico
Antes de los entornos gráficos, toda la interacción con el ordenador se realizaba a través de un entorno de consola (terminal o línea de comandos). El usuario escribía comandos de texto y el sistema respondía con texto. Sistemas como UNIX llevaban décadas funcionando así, siendo extraordinariamente potentes a pesar de su aspecto austero.
La revolución llegó con los entornos gráficos (GUI). Xerox PARC desarrolló el concepto, Apple lo popularizó con el Macintosh en 1984 y Microsoft lo extendió masivamente con Windows. Estos entornos introdujeron ventanas, iconos, menús y puntero (ratón), haciendo la informática accesible al público general.
Es importante desmitificar una confusión habitual: la multitarea no nació con los entornos gráficos. UNIX siempre fue multitarea, mucho antes de que existieran las «ventanitas». Los entornos gráficos simplemente hicieron visible lo que el sistema operativo ya hacía internamente.
🔹 Java en consola y en entorno gráfico
Java permite desarrollar aplicaciones tanto de consola como gráficas. Los programas de consola usan System.out para imprimir y Scanner para leer entrada. Las aplicaciones gráficas se construyen con Swing (clásica) o JavaFX (moderna).
// Programa de consola: saludo personalizado import java.util.Scanner; public class SaludoConsola { public static void main(String[] args) { Scanner teclado = new Scanner(System.in); System.out.print("¿Cómo te llamas? "); String nombre = teclado.nextLine(); System.out.println("¡Hola, " + nombre + "! Bienvenido al curso de Java."); teclado.close(); } }
$ java SaludoConsola
¿Cómo te llamas? Ana
¡Hola, Ana! Bienvenido al curso de Java.
🏛️ Arquitecturas informáticas
La evolución histórica de la informática se refleja en las arquitecturas utilizadas para organizar los recursos de computación. Cada una responde a las necesidades técnicas y económicas de su época.
| Época | Arquitectura | Características | Ejemplo actual |
|---|---|---|---|
| Años 60 | Sistemas centralizados (mainframes) | Un ordenador central potente con múltiples terminales. Todo el procesamiento lo realiza el mainframe | Sistemas bancarios con IBM z/Series |
| Años 80 | Sistemas en red | Ordenadores independientes interconectados que comparten recursos. El usuario sabe dónde está cada recurso | Red local de oficina con servidor de archivos |
| Años 90+ | Sistemas distribuidos | Múltiples estaciones donde el acceso a recursos es transparente | Servicios en la nube (AWS, Azure, Google Cloud) |
| Actualidad | Sistemas paralelos | Varios procesadores operan conjuntamente sobre el mismo problema | Supercomputadores, GPUs para machine learning |
🏗️ Ejemplo completo integrador en Java
El siguiente programa combina los conceptos fundamentales del artículo: procesos, hilos, estados y paradigma orientado a objetos. Simula un planificador de procesos simplificado que gestiona la ejecución de tareas.
import java.util.ArrayList; import java.util.List; /** * Simulador de planificador de procesos. * Demuestra: POO (clases, objetos, encapsulamiento), * estados de proceso, y lógica imperativa. */ public class SimuladorProcesos { enum Estado { PREPARADO, EN_EJECUCION, BLOQUEADO, FINALIZADO } static class Proceso { private String nombre; private int ciclosRestantes; private Estado estado; public Proceso(String nombre, int ciclos) { this.nombre = nombre; this.ciclosRestantes = ciclos; this.estado = Estado.PREPARADO; } public void ejecutarCiclo() { if (estado == Estado.PREPARADO) estado = Estado.EN_EJECUCION; if (estado == Estado.EN_EJECUCION) { ciclosRestantes--; if (ciclosRestantes <= 0) estado = Estado.FINALIZADO; } } public void bloquear() { if (estado == Estado.EN_EJECUCION) estado = Estado.BLOQUEADO; } public void desbloquear() { if (estado == Estado.BLOQUEADO) estado = Estado.PREPARADO; } public boolean haFinalizado() { return estado == Estado.FINALIZADO; } public String toString() { return String.format("%-12s [%s] ciclos: %d", nombre, estado, Math.max(0, ciclosRestantes)); } } public static void main(String[] args) { List<Proceso> procesos = new ArrayList<>(); procesos.add(new Proceso("Navegador", 3)); procesos.add(new Proceso("Editor", 2)); procesos.add(new Proceso("Compilador", 4)); System.out.println("=== Simulador de Planificador de Procesos ==="); System.out.println("Estado inicial:"); for (Proceso p : procesos) System.out.println(" " + p); int ciclo = 1; while (!todosFinalizados(procesos)) { System.out.println("\n--- Ciclo " + ciclo + " ---"); for (Proceso p : procesos) { if (!p.haFinalizado()) { if (ciclo % 2 == 0 && p.nombre.equals("Navegador") && p.estado != Estado.BLOQUEADO) { p.bloquear(); System.out.println(" [BLOQUEADO] " + p.nombre + " espera E/S"); } else if (p.estado == Estado.BLOQUEADO) { p.desbloquear(); System.out.println(" [DESBLOQUEADO] " + p.nombre); } else { p.ejecutarCiclo(); System.out.println(" " + p); } } } ciclo++; } System.out.println("\n=== Todos los procesos han finalizado ==="); } private static boolean todosFinalizados(List<Proceso> procesos) { for (Proceso p : procesos) if (!p.haFinalizado()) return false; return true; } }
$ java SimuladorProcesos
=== Simulador de Planificador de Procesos ===
Estado inicial:
Navegador [PREPARADO] ciclos: 3
Editor [PREPARADO] ciclos: 2
Compilador [PREPARADO] ciclos: 4
--- Ciclo 1 ---
Navegador [EN_EJECUCION] ciclos: 2
Editor [EN_EJECUCION] ciclos: 1
Compilador [EN_EJECUCION] ciclos: 3
--- Ciclo 2 ---
[BLOQUEADO] Navegador espera E/S
Editor [FINALIZADO] ciclos: 0
Compilador [EN_EJECUCION] ciclos: 2
--- Ciclo 3 ---
[DESBLOQUEADO] Navegador
Compilador [EN_EJECUCION] ciclos: 1
--- Ciclo 4 ---
[BLOQUEADO] Navegador espera E/S
Compilador [FINALIZADO] ciclos: 0
--- Ciclo 5 ---
[DESBLOQUEADO] Navegador
--- Ciclo 6 ---
Navegador [EN_EJECUCION] ciclos: 1
--- Ciclo 7 ---
Navegador [FINALIZADO] ciclos: 0
=== Todos los procesos han finalizado ===
Puntos clave: Se utiliza una enum para representar los estados (tipo seguro). La clase Proceso encapsula su estado interno. El método main actúa como planificador, reflejando a escala reducida cómo opera un sistema operativo real.
⚠️ Errores frecuentes de principiante
❌ Error 1: Confundir programa con proceso
// INCORRECTO: «Tengo un proceso guardado en el disco duro» // En disco se almacenan PROGRAMAS (archivos .class, .jar, .exe) // Un proceso solo existe cuando el programa se está ejecutando en memoria // CORRECTO: // «Tengo un programa (MiApp.class) que, al ejecutarse, crea un proceso»
❌ Error 2: Creer que multiprogramación requiere múltiples CPUs
// INCORRECTO: «Para ejecutar varios programas necesito varias CPUs» // La multiprogramación funciona con UNA SOLA CPU, // intercalando la ejecución de procesos. // CORRECTO: // Multiprogramación = 1 CPU, varios procesos (concurrencia) // Multiprocesamiento = varias CPUs, varios procesos (paralelismo)
❌ Error 3: Pensar que POO y programación imperativa son opuestos
// INCORRECTO: «Java es orientado a objetos, así que no es imperativo» // La POO INCLUYE programación imperativa dentro de los métodos public class Ejemplo { public void calcular() { int resultado = 0; // variable (imperativo) for (int i = 0; i < 10; i++) { // bucle (imperativo) resultado += i; } // La POO organiza en clases y objetos, // pero DENTRO de cada método se programa imperativamente } }
❌ Error 4: Confundir sistema en red con sistema distribuido
// Sistema en RED: // El usuario sabe dónde están los recursos // Ejemplo: \\servidor\documentos\informe.pdf // Sistema DISTRIBUIDO: // El acceso es transparente // Ejemplo: Google Drive → los archivos pueden estar en // cualquier datacenter del mundo, pero se ven «locales»
✏️ Ejercicios prácticos
Ejercicio 1: Identificar estados de proceso
¿Qué imprime el siguiente programa? Identifica en qué estado se encontraría el proceso en cada línea clave.
public class EstadosProceso { public static void main(String[] args) throws InterruptedException { System.out.println("A: Proceso en ejecución"); Thread.sleep(1000); System.out.println("B: Proceso reanudado"); int resultado = 0; for (int i = 0; i < 5; i++) { resultado += i; } System.out.println("C: Resultado = " + resultado); } }
Ejercicio 2: Comparar paradigmas imperativo y POO
Escribe dos versiones de un programa que calcule el área de un rectángulo de base 5 y altura 3. La primera versión puramente imperativa (sin clases adicionales). La segunda con POO: clase Rectangulo con atributos base y altura, y método calcularArea().
Ejercicio 3: Simulador de cola de procesos
Diseña un programa con una clase Tarea (nombre, duración en ciclos) y un main que cree un array de 4 tareas, las ejecute con algoritmo round-robin (1 ciclo por turno, luego al final de la cola) y muestre el orden de finalización.
❓ Preguntas frecuentes sobre Conceptos fundamentales de programación
Las dudas más comunes respondidas de forma clara y directa.
💬 Foro de discusión
¿Tienes dudas sobre Conceptos fundamentales de programación? Comparte tu pregunta con la comunidad.
Todavía no hay mensajes. ¡Sé el primero en participar!