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

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

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

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

线程

定义

线程(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 秒钟后销毁这个进程。

目录
相关文章
|
8月前
|
存储 Linux API
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
在计算机系统的底层架构中,操作系统肩负着资源管理与任务调度的重任。当我们启动各类应用程序时,其背后复杂的运作机制便悄然展开。程序,作为静态的指令集合,如何在系统中实现动态执行?本文带你一探究竟!
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
|
6月前
|
并行计算 Linux
Linux内核中的线程和进程实现详解
了解进程和线程如何工作,可以帮助我们更好地编写程序,充分利用多核CPU,实现并行计算,提高系统的响应速度和计算效能。记住,适当平衡进程和线程的使用,既要拥有独立空间的'兄弟',也需要在'家庭'中分享和并行的成员。对于这个世界,现在,你应该有一个全新的认识。
250 67
|
7月前
|
SQL 监控 网络协议
YashanDB进程线程体系
YashanDB进程线程体系
|
8月前
|
消息中间件 Linux C++
c++ linux通过实现独立进程之间的通信和传递字符串 demo
的进程间通信机制,适用于父子进程之间的数据传输。希望本文能帮助您更好地理解和应用Linux管道,提升开发效率。 在实际开发中,除了管道,还可以根据具体需求选择消息队列、共享内存、套接字等其他进程间通信方
167 16
|
8月前
|
数据采集 Java 数据处理
Python实用技巧:轻松驾驭多线程与多进程,加速任务执行
在Python编程中,多线程和多进程是提升程序效率的关键工具。多线程适用于I/O密集型任务,如文件读写、网络请求;多进程则适合CPU密集型任务,如科学计算、图像处理。本文详细介绍这两种并发编程方式的基本用法及应用场景,并通过实例代码展示如何使用threading、multiprocessing模块及线程池、进程池来优化程序性能。结合实际案例,帮助读者掌握并发编程技巧,提高程序执行速度和资源利用率。
363 0
|
9月前
|
Java Linux 调度
硬核揭秘:线程与进程的底层原理,面试高分必备!
嘿,大家好!我是小米,29岁的技术爱好者。今天来聊聊线程和进程的区别。进程是操作系统中运行的程序实例,有独立内存空间;线程是进程内的最小执行单元,共享内存。创建进程开销大但更安全,线程轻量高效但易引发数据竞争。面试时可强调:进程是资源分配单位,线程是CPU调度单位。根据不同场景选择合适的并发模型,如高并发用线程池。希望这篇文章能帮你更好地理解并回答面试中的相关问题,祝你早日拿下心仪的offer!
183 6
|
7月前
|
Linux 数据库 Perl
【YashanDB 知识库】如何避免 yasdb 进程被 Linux OOM Killer 杀掉
本文来自YashanDB官网,探讨Linux系统中OOM Killer对数据库服务器的影响及解决方法。当内存接近耗尽时,OOM Killer会杀死占用最多内存的进程,这可能导致数据库主进程被误杀。为避免此问题,可采取两种方法:一是在OS层面关闭OOM Killer,通过修改`/etc/sysctl.conf`文件并重启生效;二是豁免数据库进程,由数据库实例用户借助`sudo`权限调整`oom_score_adj`值。这些措施有助于保护数据库进程免受系统内存管理机制的影响。
|
7月前
|
Linux Shell
Linux 进程前台后台切换与作业控制
进程前台/后台切换及作业控制简介: 在 Shell 中,启动的程序默认为前台进程,会占用终端直到执行完毕。例如,执行 `./shella.sh` 时,终端会被占用。为避免不便,可将命令放到后台运行,如 `./shella.sh &`,此时终端命令行立即返回,可继续输入其他命令。 常用作业控制命令: - `fg %1`:将后台作业切换到前台。 - `Ctrl + Z`:暂停前台作业并放到后台。 - `bg %1`:让暂停的后台作业继续执行。 - `kill %1`:终止后台作业。 优先级调整:
352 5
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
弹性计算 Linux 区块链
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
428 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)