JUC(一):进程与线程

一、进程与线程

1.1 进程

进程(Process) 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。 在当代面向线程设计的计算机结构中,进程是线程的容器程序是指令、数据及其组织形式的 描述进程是程序的实体

1.2 线程

线程(thread) 是操作系统能够进行运算调度的最小单位。它被包含在进程之 中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流, 一个进程中可以并发多个线程,每条线程并行执行不同的任务

1、线程是独立调度和分派的基本单位。2、同一进程中的多条线程将共享该进程中的全部系统资源。3、一个进程可以有很多线程,每条线程并行执行不同的任务。可并发执行。

1.3 创建线程的几种方式

  1. 继承Thread类:创建一个类继承Thread类,并重写run()方法,在run()方法中定义线程要执行的任务。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class MyThread extends Thread {
    public void run() {
    System.out.println("This is a thread created by extending Thread class.");
    }

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

  2. 实现Runnable接口:创建一个类实现Runnable接口,并实现其run()方法,在run()方法中定义线程要执行的任务。然后将实现了Runnable接口的类作为参数传递给Thread类的构造方法创建线程。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class MyRunnable implements Runnable {
    public void run() {
    System.out.println("This is a thread created by implementing Runnable interface.");
    }

    public static void main(String[] args) {
    MyRunnable myRunnable = new MyRunnable();
    Thread thread = new Thread(myRunnable);
    thread.start();
    }
    }
  3. 使用匿名内部类:可以直接在创建线程的地方使用匿名内部类来实现线程,省去了定义一个单独的类的步骤。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class AnonymousThread {
    public static void main(String[] args) {
    Thread thread = new Thread(new Runnable() {
    public void run() {
    System.out.println("This is a thread created using anonymous inner class.");
    }
    });
    thread.start();
    }
    }

  4. 使用线程池:通过Executor框架可以创建线程池,然后将任务提交给线程池执行,可以重复利用线程,提高性能。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;

    public class ThreadPoolExample {
    public static void main(String[] args) {
    ExecutorService executor = Executors.newFixedThreadPool(5);
    for (int i = 0; i < 10; i++) {
    Runnable worker = new MyRunnable();
    executor.execute(worker);
    }
    executor.shutdown();
    while (!executor.isTerminated()) {
    }
    System.out.println("All threads are finished");
    }
    }
  5. 使用Callable和Future:Callable接口类似于Runnable接口,但是它可以返回结果,并且可以抛出异常。Future接口可以获取Callable的返回结果,可以用于异步获取线程执行结果。

    步骤1:创建实现Callable接口的类

    步骤2:创建一个类对象:Callable callable = new Callable();

    步骤3:由Callable创建一个FutureTask对象:

    FutureTask future = new FutureTask(callable);

    注释:FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了Future和Runnable接口。

    步骤4:由FutureTask创建一个Thread对象:Thread thread = new Thread(future);

    ​ 这里为什么可以new Thread(future)

    ​ Thread的构造函数:

    1
    2
    3
    public Thread(Runnable target) {
    this(null, target, "Thread-" + nextThreadNum(), 0);
    }
    1
    2
    3
    public class FutureTask<V> implements RunnableFuture<V> 

    public interface RunnableFuture<V> extends Runnable, Future<V>

    因为FutureTask间接实现了Runnable接口

    步骤5:启动线程:thread.start();

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;

    public class CallableExample {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
    Callable<String> callable = new Callable<String>() {
    public String call() {
    return "This is a thread created using Callable and Future";
    }
    };
    FutureTask<String> future = new FutureTask<String>(callable);
    Thread thread = new Thread(future);
    thread.start();
    System.out.println(future.get());
    }
    }

1.4 Callable和Runnable

实现了Runnable和Callable的类都可以当作线程任务,那么这俩接口有什么区别:

  1. Runnable接口是java1.1就有的,Callable接口是java1.5才有的,可以认为Callable接口是升级版的Runnable接口;
  2. Runnable接口里面的线程任务是在run方法里面,而Callable接口是call方法
  3. Callable接口的任务执行后会有返回值,Runnable接口的任务无返回值(void);
  4. callable接口中的call方法支持抛出异常,而run方法不可以
  5. 加入线程池运行,Runnable使用ExecutorService的execute方法,Callable使用ExecutorService的submit方法
  6. 运行Callable任务可以拿到一个Future对象,表示异步计算的结果。Future对象封装了检查计算是否完成、检索计算的结果的方法,而Runnable接口没有。

总的来说:Callable类似于Runnable,但它可以返回一个结果,而且还能抛出异常。常用于与线程池组合使用,组成了一套java提供的异步框架。