线程

线程的基本概念

并行和并发

  • 并行:多个CPU核心同时工作,处理不同的任务。
  • 并发:多个任务交替使用 CPU 核心工作,以提高 CPU 利用率。

进程和线程

进程 Processor

程序的一次执行。由操作系统创建并分配资源,执行一个单独的任务。

进程是系统进行资源分配和调度的独立单位,每个进程都有自己的内存空间和系统资源。进程内所有线程共享堆存储空间,保存程序中定义的对象和常量池。

Windows系统中,每个运行的 Java 程序都是一个独立的进程。

线程 Thread

进程内的执行单元,不分配单独的资源,执行一个单独的子任务。

线程是进程内调度和分派的基本单位,共享进程资源。每个线程有自己的独立的栈存储空间,保存线程执行的方法以及基本类型的数据。

运行的 Java 程序内含至少一个主线程 main ,用户可以在 Java 程序中自定义并调用多个线程。 JVM 垃圾回收线程也是一个独立的线程。


Thread 类

Thread 类是系统自带的线程类,实现了 Runnable 接口。

线程定义

Runnable 接口内唯一声明了 run 方法,由 Thread 类实现。开发者在 run 方法中定义运行时线程将要执行的功能,线程开启后由 JVM 自动调用并执行。如果开发者主动调用 run 方法,只会当作普通方法执行。

开发者可以通过以下三种方式自定义线程类:

  1. 继承 Thread 类,重写 run 方法。
public class MyThread extends Thread {
   @Override
   public void run() {
       System.out.println(Thread.currentThread().getName());
   }
}

  1. 实现 Runnable 接口,实现 run 方法。推荐使用,避免了单继承的局限性。缺点是 如果线程有执行结果是不可以直接返回的。
public class MyThread implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

  1. 实现 Callable 接口,实现 call 方法。 用FutureTask把Callable对象封装成线程任务对象,这种方式的优点是可以得到线程执行的结果。
// 1.定义一个任务类 实现Callable接口  应该申明线程任务执行完毕后的结果的数据类型
class MyCallable implements Callable<String>{
    private int n;
    public MyCallable(int n) {
        this.n = n;
    }

    // 2.重写call方法(任务方法)
    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= n ; i++) {
            sum += i;
        }
        return "子线程执行的结果是:" + sum;
    }
}

public class ThreadDemo3 {
    public static void main(String[] args) {
      // 3.创建Callable任务对象
      Callable<String> call = new MyCallable(100);

      // 4.把Callable任务对象 交给 FutureTask 对象
      // FutureTask对象的作用1: 是Runnable的对象(实现了Runnable接口),可以交给Thread了
      // FutureTask对象的作用2: 可以在线程执行完毕之后通过调用其get方法得到线程执行完成的结果
      FutureTask<String> f = new FutureTask<>(call);

      // 5.交给线程处理
      Thread t = new Thread(f);

      //6. 启动线程
      t.start();

      try {
           // 如果f任务没有执行完毕,这里的代码会等待,直到线程跑完才提取结果。
           String rs = f.get();
           System.out.println("第一个结果:" + rs);
      } catch (Exception e) {
           e.printStackTrace();
      }
   }
}

线程启动

Thread 类定义了 start 方法。调用 start 方法后,系统会开启一个新线程进入就绪状态:由 JVM 会自动对线程进行调度,在运行时调用并执行线程的 run 方法。一个线程只能启动一次。

  1. 如果自定义线程类继承 Thread 类,直接启动。
public class Main {
   public static void main(String[] args) {
       MyThread t1 = new MyThread();
       MyThread t2 = new MyThread("ThreadName");
       t1.start();
       t2.start();
   }
}

  1. 如果自定义线程类实现 Runnable 接口,则需要借助 Thread 类启动线程。
public class Main {
   public static void main(String[] args) {
       MyThread mythread = new MyThread();
       Thread t1 = new Thread(mythread);                   // 由系统指定默认线程名 Thread-X
       Thread t2 = new Thread(mythread, "ThreadName");     // 开发者自定义线程名 
       t1.start();
       t2.start();
   }
}

  1. 同时定义和启动线程,通过匿名内部类方式,我们可以实现同时定义和启动线程的简洁写法。
public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable(){
            public void run(){
                System.out.println(Thread.currentThread().getName());
            }
        }).start();
    }
}

// Lambda 表达式简写一

public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println(Thread.currentThread().getName());
        }).start();
    }
}

// Lambda 表达式简写二

public class Main {
   public static void main(String[] args) {
       Test test = new test();
       Thread t = new Thread(test::method);         
       t.start();
   }
}

class Test {
   public void method() {
       System.out.println(Thread.currentThread().getName()); // 获取当前线程对象名字
   }
}

posted @ 2023-03-23 16:08  晚点心动。  阅读(19)  评论(0)    收藏  举报