文章目录

  • 1 并发和并行
  • 2 进程和线程
  • 2.1 进程与线程的区别
  • 3 创建进程
  • 3.1 在 Java 中创建进程
  • 3.1.1 通过 Runtime 类的 exec() 方法
  • 3.1.2 ProcessBuilder 创建进程
  • 4 创建线程
  • 4.1 继承Thead类
  • 4.2 Runnable接口
  • 4.3 使用Callable和Future
  • 5 三种方式的对比
  • 5.1 实现Runnable/Callable接口相比继承Thread类的优势
  • 5.2 Callable和Runnable的区别


1 并发和并行

并行:指两个或多个时间在同一时刻发生(同时发生);

并发:指两个或多个事件在一个时间段内发生。

多核处理器,核越多,并行处理的程序越多,能大大的提高电脑运行的效率;单核处理器,CPU时间片切换使用,并发执行程序。

2 进程和线程

进程:每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。

线程:进程内部的一个独立执行单元;一个进程可以同时并发的运行多个线程,可以理解为一个进程便相当于一个单 CPU 操作系统,而线程便是这个系统中运行的多个任务。

2.1 进程与线程的区别

进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。

线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。

进程之间不能共享内存,而线程之间可以共享内存。

3 创建进程

3.1 在 Java 中创建进程

在 windows 操作系统中,创建一个进程通常就是打开某个应用软件。

3.1.1 通过 Runtime 类的 exec() 方法

Runtime: 允许应用程序和应用正在运行的环境交互

Runtime run = Runtime.getRuntime();
//打开记事本
run.exec("notepad");
3.1.2 ProcessBuilder 创建进程

ProcessBuilder用来创建一个操作系统进程。

public static void main(String[] args) throws IOException {
       // 执行命令 
        ProcessBuilder pb = new ProcessBuilder("ls");
        Process process = pb.start();
        String line;
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK"));
        while ((line = reader.readLine()) != null) {
            System.out.println(line);
        }
        reader.close();
    }public static void main(String[] args) throws IOException {
        ProcessBuilder pb = new ProcessBuilder("ls");
        Process process = pb.start();
        String line;
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK"));
        while ((line = reader.readLine()) != null) {
            System.out.println(line);
        }
        reader.close();
        // 打开应用程序
        ProcessBuilder p = new ProcessBuilder("C:/Program Files/Notepad++/Notepad++.exe");
        p.start();
}

4 创建线程

Java中创建线程主要有三种方式:
1.继承Thread类
2.实现Runnable接口
3.使用Callable和Future

4.1 继承Thead类
  • 继承Thread类并重写run方法
  • 创建线程对象
  • 调用该线程对象的start()方法来启动线程
public class CreateThreadTest {
    public static void main(String[] args) {
        new ThreadTest().start();
    }
}

class ThreadTest extends Thread{
    @Override
    public void run() {
    
    }
}
4.2 Runnable接口
  • 定义一个类实现Runnable接口,并重写该接口的run()方法
  • 创建 Runnable实现类的对象,作为创建Thread对象的target参数,此Thread对象才是真正的线程对象
  • 调用线程对象的start()方法来启动线程
public class CreateThreadTest {
    public static void main(String[] args) {
        RunnableTest runnableTest = new RunnableTest();
        new Thread(runnableTest, "线程名").start();
    }
}

class RunnableTest implements Runnable{
    @Override
    public void run() {
      
    }
}

注意:启动线程是调用 start() 方法,而不是调用 run() 方法。

4.3 使用Callable和Future

Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大:call()方法可以有返回值,可以声明抛出异常。Callable是一个函数式接口

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

Future接口来接收Callable接口中call()方法的返回值。

Java 线程到底是并行还是并发 线程是java程序的并行机制_Java 线程到底是并行还是并发

使用Callable和Future创建线程的步骤如下:

  • 定义一个类实现Callable接口,并重写call()方法,该call()方法将作为线程执行体,并且有返回值
  • 创建Callable实现类的实例,使用FutureTask类来包装Callable对象
  • 使用FutureTask对象作为Thread对象的target创建并启动线程
  • 用FutureTask对象的get()方法来获得子线程执行结束后的返回值
public class CallableTest {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName() + " is running");
        FutureTask<Integer> futureTask = new FutureTask<>(new MyCallable());
        new Thread(futureTask).start();

        try {
            System.out.println("子线程的返回值: " + futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i <= 100; i++) {
            sum += i;
        }
        System.out.println(Thread.currentThread().getName() + " is running: " + sum);
        return sum;
    }
}

5 三种方式的对比

5.1 实现Runnable/Callable接口相比继承Thread类的优势
  • 适合多个线程进行资源共享
  • 可以避免java中单继承的限制
  • 增加程序的健壮性,代码和数据独立
  • 线程池只能放入Runable或Callable接口实现类,不能直接放入继承Thread的类
5.2 Callable和Runnable的区别
  • Callable重写的是call()方法,Runnable重写的方法是run()方法
  • call()方法执行后可以有返回值,run()方法没有返回值
  • call()方法可以抛出异常,run()方法不可以
  • 运行Callable任务可以拿到一个Future对象,表示异步计算的结果 。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果

参考:

Java创建线程的三种方式及对比

[Java 多线程详解(二)------如何创建进程和线程