什么是线程和进程?是如何创建、同步、通信、销毁的?

简介: 什么是线程和进程?是如何创建、同步、通信、销毁的?

计算机系统中,线程和进程是两个基本的概念。多线程编程已经成为现代编程中比较常见的技术,因此对于线程和进程的深刻理解变得尤为重要。

本文将详细介绍线程和进程,包括定义、创建、同步、通信、销毁等方面的内容,并通过实例帮助读者更好地了解这两个概念。

线程

定义

线程(Thread)是指在单个程序中同时执行的一段指令流或执行流程。一个进程可以包含多个线程,每个线程可以执行不同的任务。

在 Java 中,线程是虚拟机中的一种轻量级对象,每个线程拥有自己的执行堆栈和程序计数器(Program Counter,PC),可以独立执行任务。

创建线程

Java 中创建线程有两种方式:

继承 Thread 类并重写 run 方法

public class MyThread extends Thread {
   
    public void run() {
   
        System.out.println("MyThread running");
    }
}

public static void main(String[] args) {
   
    Thread myThread = new MyThread();
    myThread.start();
}

上面的代码创建了一个新的线程对象 myThread,并启动这个线程。在 MyThread 类中重写 run 方法是为了定义这个线程的执行逻辑。

实现 Runnable 接口并通过 Thread 类创建线程

public class MyRunnable implements Runnable {
   
    public void run() {
   
        System.out.println("MyRunnable running");
    }
}

public static void main(String[] args) {
   
    MyRunnable myRunnable = new MyRunnable();
    Thread myThread = new Thread(myRunnable);
    myThread.start();
}

上面的代码创建了一个实现 Runnable 接口的类 MyRunnable,并通过这个类创建了一个线程对象 myThread。在 MyRunnable 类中实现 run 方法是为了定义这个线程的执行逻辑。

线程同步

线程同步是指在多个线程之间协调执行的机制。当多个线程同时访问共享资源时,可能会出现数据不一致的情况。为了避免这种情况,需要使用同步机制来保证数据的一致性。

互斥锁

互斥锁(Mutex)是最常用的一种同步机制。互斥锁可以保证在任何时刻只有一个线程能够进入临界区(Critical Section),从而避免多个线程同时访问共享资源的问题。

在 Java 中,可以使用 synchronized 关键字来实现互斥锁。synchronized 关键字可以应用于方法或者某个代码块,以确保在同一时间只有一个线程可以访问这个方法或者代码块中的内容。

public class Counter {
   
    private int count;

    public synchronized void increment() {
   
        count++;
    }

    public synchronized int getCount() {
   
        return count;
    }
}

上面的代码定义了一个类 Counter,其中的 incrementgetCount 方法都使用了 synchronized 关键字。这样可以保证在任何时刻只有一个线程能够同时访问这两个方法,从而避免发生数据不一致的情况。

条件变量

条件变量是一种同步机制,可以用于多个线程之间的通信。条件变量通常结合互斥锁一起使用,在等待条件时会释放互斥锁,以便其他线程也可以获取到互斥锁。

在 Java 中,可以使用 waitnotify 方法来实现条件变量。wait 方法可以使当前线程进入等待状态,直到其他线程调用 notify 或者 notifyAll 方法来唤醒这个线程。notify 方法可以随机唤醒一个正在等待的线程,而 notifyAll 方法可以唤醒所有正在等待的线程。

public class MessageQueue {
   
    private List<String> queue = new ArrayList<>();

    public synchronized void put(String message) {
   
        queue.add(message);
        notify();
    }

    public synchronized String take() throws InterruptedException {
   
        while (queue.isEmpty()) {
   
            wait();
        }
        return queue.remove(0);
    }
}

上面的代码定义了一个类 MessageQueue,其中的 put 方法和 take 方法分别用于向队列中添加消息和取出消息。在 take 方法中,如果队列中没有消息,则会进入等待状态,并通过 wait 方法释放互斥锁。在 put 方法中,如果队列中有新的消息,则会通知正在等待的线程从等待状态中唤醒。

线程通信

线程通信是指在多个线程之间传递信息或者数据的机制。线程通信可以通过共享内存或者消息传递来实现。

共享内存

共享内存是一种线程通信的方式,可以让多个线程访问同一块内存区域。在使用共享内存时,需要使用互斥锁来保证数据的一致性。

public class Counter {
   
    private int count;

    public synchronized void increment() {
   
        count++;
        notifyAll();
    }

    public synchronized int getCount() throws InterruptedException {
   
        while (count == 0) {
   
            wait();
        }
        int result = count;
        count = 0;
        return result;
    }
}

上面的代码定义了一个类 Counter,其中的 increment 方法用于增加计数器的值,而 getCount 方法用于获取计数器当前的值,并将计数器清零。在 getCount 方法中,如果计数器的值为零,则会进入等待状态,等待其他线程调用 increment 方法来唤醒这个线程。

消息传递

消息传递是一种线程通信的方式,可以让多个线程之间通过消息来进行通信。消息传递有两种方式:共享队列和直接通信。

共享队列

共享队列是一种消息传递的方式,多个线程可以通过一个公共的队列来发送和接收消息。在使用共享队列时,需要使用互斥锁和条件变量来保证数据的一致性。

public class MessageQueue {
   
    private List<String> queue = new ArrayList<>();

    public synchronized void put(String message) {
   
        queue.add(message);
        notify();
    }

    public synchronized String take() throws InterruptedException {
   
        while (queue.isEmpty()) {
   
            wait();
        }
        return queue.remove(0);
    }
}

上面的代码定义了一个类 MessageQueue,其中的 put 方法和 take 方法分别用于向队列中添加消息和取出消息。在 take 方法中,如果队列中没有消息,则会进入等待状态,并通过 wait 方法释放互斥锁。在 put 方法中,如果队列中有新的消息,则会通知正在等待的线程从等待状态中唤醒。

直接通信

直接通信是一种消息传递的方式,多个线程之间通过直接发送消息来进行通信。在使用直接通信时,常用的方式有管道、套接字和消息队列等。

以管道为例,可以使用 PipedInputStreamPipedOutputStream 来实现两个线程之间的通信。

public class PipeDemo {
   
    public static void main(String[] args) throws Exception {
   
        PipedOutputStream output = new PipedOutputStream();
        PipedInputStream input = new PipedInputStream(output);

        Thread t1 = new Thread(() -> {
   
            try {
   
                output.write("Hello World".getBytes());
            } catch (IOException e) {
   
                e.printStackTrace();
            }
        });

        Thread t2 = new Thread(() -> {
   
            try {
   
                byte[] buffer = new byte[1024];
                int len = input.read(buffer);
                System.out.println(new String(buffer, 0, len));
            } catch (IOException e) {
   
                e.printStackTrace();
            }
        });

        t1.start();
        t2.start();
    }
}

上面的代码定义了一个类 PipeDemo,该类通过 PipedOutputStreamPipedInputStream 实现了两个线程之间的通信。在 t1 线程中,向管道中写入了一条消息 "Hello World";在 t2 线程中,从管道中读取了该消息,并输出到控制台。

销毁线程

线程可以通过调用 interrupt 方法来中断执行,也可以通过设置 volatile 类型的标志位来通知线程退出。当线程不再需要时,可以使用 join 方法等待线程执行完毕并回收资源。

public class StopThreadDemo {
   
    private static volatile boolean stop = false;

    public static void main(String[] args) throws InterruptedException {
   
        Thread t = new Thread(() -> {
   
            while (!stop) {
   
                // do something
            }
        });
        t.start();
        Thread.sleep(1000);
        stop = true;
        t.join();
    }
}

上面的代码定义了一个类 StopThreadDemo,该类使用 volatile 类型的标志位来通知线程退出。

进程

定义

进程(Process)是计算机中的一个程序关于某个数据集合上的一次运行活动。一个进程可以包含多个线程,每个线程可以执行不同的任务。

在 Java 中,一个进程通常由多个线程组成,可以使用 java.lang.ProcessBuilder 类来创建和控制进程。

创建进程

Java 中可以使用 java.lang.ProcessBuilder 类来创建和控制进程。

public class ProcessDemo {
   
    public static void main(String[] args) throws Exception {
   
        ProcessBuilder builder = new ProcessBuilder("ls", "-lah");
        Process process = builder.start();

        InputStream inputStream = process.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        String line;
        while ((line = reader.readLine()) != null) {
   
            System.out.println(line);
        }

        int exitCode = process.waitFor();
        System.out.println("Exit code: " + exitCode);
    }
}

上面的代码创建了一个进程,并执行了 ls -lah 命令。通过读取进程的输入流,可以获取命令执行后的输出结果。调用 waitFor 方法可以等待进程执行完毕并获取进程的退出码。

进程同步

进程同步是指在多个进程之间协调执行的机制。当多个进程同时访问共享资源时,可能会出现数据不一致的情况。为了避免这种情况,需要使用同步机制来保证数据的一致性。

在 Java 中,可以使用管道、共享内存等方式来实现进程同步。以管道为例,可以使用 PipedInputStreamPipedOutputStream 来实现两个进程之间的通信。

进程通信

进程通信是指在多个进程之间传递信息或者数据的机制。进程通信可以通过共享内存或者消息传递来实现。

以管道为例,可以使用 PipedInputStreamPipedOutputStream 来实现两个进程之间的通信。

public class PipeDemo {
   
    public static void main(String[] args) throws Exception {
   
        Process p1 = new ProcessBuilder("echo", "Hello World").start();
        Process p2 = new ProcessBuilder("tr", "a-z", "A-Z").start();

        PipedOutputStream output = new PipedOutputStream(p1.getOutputStream());
        PipedInputStream input = new PipedInputStream(p2.getInputStream());
        p1.getOutputStream().close();
        p2.getInputStream().close();

        byte[] buffer = new byte[1024];
        int len;
        while ((len = input.read(buffer)) != -1) {
   
            output.write(buffer, 0, len);
        }
        output.close();

        int exitCode = p1.waitFor();
        System.out.println("Exit code: " + exitCode);
        exitCode = p2.waitFor();
        System.out.println("Exit code: " + exitCode);
    }
}

上面的代码创建了两个进程 p1p2,在 p1 进程中执行了 echo "Hello World" 命令,在 p2 进程中执行了 tr a-z A-Z 命令。通过管道将 p1 进程的输出和 p2 进程的输入连接起来,从而实现两个进程之间的通信。

销毁进程

进程可以通过调用 destroy 方法来销毁进程。当进程不再需要时,可以调用这个方法来释放所有相关资源。

public class StopProcessDemo {
   
    public static void main(String[] args) throws Exception {
   
        ProcessBuilder builder = new ProcessBuilder("ping", "-t", "localhost");
        Process process = builder.start();
        Thread.sleep(10000);
        process.destroy();
    }
}

上面的代码创建了一个进程,并在 10 秒钟后销毁这个进程。

目录
相关文章
|
10天前
|
消息中间件 并行计算 安全
进程、线程、协程
【10月更文挑战第16天】进程、线程和协程是计算机程序执行的三种基本形式。进程是操作系统资源分配和调度的基本单位,具有独立的内存空间,稳定性高但资源消耗大。线程是进程内的执行单元,共享内存,轻量级且并发性好,但同步复杂。协程是用户态的轻量级调度单位,适用于高并发和IO密集型任务,资源消耗最小,但不支持多核并行。
28 1
|
5天前
|
Java 调度
[Java]线程生命周期与线程通信
本文详细探讨了线程生命周期与线程通信。文章首先分析了线程的五个基本状态及其转换过程,结合JDK1.8版本的特点进行了深入讲解。接着,通过多个实例介绍了线程通信的几种实现方式,包括使用`volatile`关键字、`Object`类的`wait()`和`notify()`方法、`CountDownLatch`、`ReentrantLock`结合`Condition`以及`LockSupport`等工具。全文旨在帮助读者理解线程管理的核心概念和技术细节。
18 1
[Java]线程生命周期与线程通信
|
6天前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
8 1
|
6天前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
15 1
|
6天前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
13 1
|
8天前
|
Python
Python中的多线程与多进程
本文将探讨Python中多线程和多进程的基本概念、使用场景以及实现方式。通过对比分析,我们将了解何时使用多线程或多进程更为合适,并提供一些实用的代码示例来帮助读者更好地理解这两种并发编程技术。
|
10天前
|
消息中间件 并行计算 安全
进程、线程、协程
【10月更文挑战第15天】进程、线程和协程是操作系统中三种不同的执行单元。进程是资源分配和调度的基本单位,每个进程有独立的内存空间;线程是进程内的执行路径,共享进程资源,切换成本较低;协程则更轻量,由用户态调度,适合处理高并发和IO密集型任务。进程提供高隔离性和安全性,线程支持高并发,协程则在资源消耗和调度灵活性方面表现优异。
34 2
|
16天前
|
Java
|
2天前
|
Linux 调度
探索操作系统核心:进程与线程管理
【10月更文挑战第24天】在数字世界的心脏,操作系统扮演着至关重要的角色。它不仅是计算机硬件与软件之间的桥梁,更是管理和调度资源的大管家。本文将深入探讨操作系统的两大基石——进程与线程,揭示它们如何协同工作以确保系统运行得井井有条。通过深入浅出的解释和直观的代码示例,我们将一起解锁操作系统的管理奥秘,理解其对计算任务高效执行的影响。
|
14天前
|
安全 调度 C#
STA模型、同步上下文和多线程、异步调度
【10月更文挑战第19天】本文介绍了 STA 模型、同步上下文和多线程、异步调度的概念及其优缺点。STA 模型适用于单线程环境,确保资源访问的顺序性;同步上下文和多线程提高了程序的并发性和响应性,但增加了复杂性;异步调度提升了程序的响应性和资源利用率,但也带来了编程复杂性和错误处理的挑战。选择合适的模型需根据具体应用场景和需求进行权衡。

相关实验场景

更多