什么是线程

本文介绍了线程的基本概念,包括其在并发编程中的应用,为何需要线程,以及进程和线程的区别。详细讲解了Java中创建线程的不同方法,并通过示例展示了多线程如何提高运行速度。还讨论了线程安全问题和线程池与协程的概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线程(Thread)

1.1 概念

1) 线程是什么

        一个线程就是一个 "执行流",即每个线程之间都有自己的执行逻辑. 多个线程之间 "同时" 执行着多份代码.

举个例子:

        一家公司要去银行办理业务,既要进行财务转账,又要进行福利发放,还得进行缴社保。如果只有张三一个会计就会忙不过来,耗费的时间特别长。为了让业务更快的办理好,张三又找来两位同事李四、王五一起来帮助他,三个人分别负责一个事情,分别申请一个号码进行排队,自此就有了三个执行流共同完成任务,但本质上他们都是为了办理一家公司的业务。此时,我们就把这种情况称为多线程,将一个大任务分解成不同小任务,交给不同执行流就分别排队执行。其中李四、王五都是张三叫来的,所以张三一般被称为主线程(Main Thread)。

2) 为啥要有线程

首先, "并发编程" 成为 "刚需".

单核 CPU 的发展遇到了瓶颈(太小进入了量子力学,会出现不确定性). 要想提高算力, 就需要多核 CPU. 而并发编程能更充分利用多核 CPU资源.

有些任务场景需要 "等待 IO", 为了让等待 IO 的时间能够去做一些其他的工作, 也需要用到并发编程.线程是为了解决并发编程引入的机制.

其次, 虽然多进程也能实现并发编程, 但是线程比进程更轻量.                                                         创建线程比创建进程更快.                                                                                                                销毁线程比销毁进程更快.                                                                                                                调度线程比调度进程更快.

进程包含线程,同一个进程里的若干线程之间共享着内存资源和文件描述符表。每个线程被独立调度执行,每个进程都有自己的状态、优先级、上下文、记账信息

最后, 线程虽然比进程轻量, 但是人们还不满足, 于是又有了 "线程池"(ThreadPool) "协程"

(Coroutine)

3) 进程和线程的区别

进程是包含线程的. 每个进程至少有一个线程存在,即主线程。

进程和进程之间不共享内存空间. 同一个进程的线程之间共享同一个内存空间.

比如之前的多进程例子中,每个客户来银行办理各自的业务,但他们之间的票据肯定是不想让别

人知道的,否则钱不就被其他人取走了么。而上面我们的公司业务中,张三、李四、王五虽然是

不同的执行流,但因为办理的都是一家公司的业务,所以票据是共享着的。这个就是多线程和多

进程的最大区别。

        一个线程对应一个 PCB , PCB 里的状态,上下文、优先级、记账信息这些都是每个线程自己的,即各自记录各自的。但是同一个进程里面的 PCB 之间的 pid 是一样的,内存指针和描述符表也是一样的

        线程安全问题:在一个房间里有十个人吃十只鸡,其中 1 和 2 看上了同一块鸡腿,这时候谁也不让谁,就可能打起来(这两个线程崩溃了);又或者 1 先把鸡腿抢吃了,但 2 就不乐意了,于是把桌都掀了(把其他线程也搞崩溃了)。在多进程里面就不会有这样的问题,因为是分是个房间十个人各自吃各自的(但是这样资源消耗大)。线程安全问题的罪魁祸首就是抢占式执行,随即调度的原因。

进程是操作系统分配资源的最小单位,线程是操作系统调度的最小单位。

4) Java 的线程 和 操作系统线程 的关系

线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对用户层提供了一些 API 供用户使用(例如 Linux 的 pthread 库).

Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进行了进一步的抽象和封装.

1.2 第一个多线程程序

感受多线程程序和普通程序的区别:

每个线程都是一个独立的执行流

多个线程之间是 "并发" 执行的.

package Thread;

import java.util.Random;

class MyThread extends Thread{
    @Override
    public void run() {
        while (true){
            System.out.println("Hello world");
            // 为了方便看,每次执行完休眠1s
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread t = new MyThread();
        t.start();
        while (true){
            System.out.println("World Hello");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

使用 jconsole 命令观察线程

1.3 创建线程

方法1 继承 Thread

1) 继承 Thread 来创建一个线程类.

2) 创建 MyThread 类的实例

3) 调用 start 方法启动线程

就是上面第一个多线程的代码

方法2 实现 Runnable 接口

1) 实现 Runnable 接口

package Thread;

// Runnable作用是描述一个“要执行的任务” ,run方法就是任务的执行细节
class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("Hello Thread");
    }
}

public class ThreadDemo2 {
    public static void main(String[] args) {
        //这里只是描述了这个任务
        Runnable runnable = new MyRunnable();
        //把任务交给线程来执行
        Thread t = new Thread(runnable);
        t.start();
    }
}

方法3 匿名内部类创建 Thread 子类对象

package Thread;

//使用匿名内部类来创建线程
public class ThreadDemo3 {
    public static void main(String[] args) {
        Thread t = new Thread(){
            //1.创建了一个Thread的子类(子类没有名字)所以叫做“匿名”
            //2.创建了子类的实例,并让t引用只想该实例
            @Override
            public void run() {
                System.out.println("Hello");
            }
        };
        t.start();
    }
}

方法4 匿名内部类创建 Runnable 子类对象

package Thread;

public class ThreadDemo4 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            //这里的写法和2本质相同,只不过是把实现Runnable任务交给匿名内部类
            //此处是创建了一个类,实现Runnable,同时创建了类的实例,并且传给了Thread的构造方法
            @Override
            public void run() {
                System.out.println("hello");
            }
        });
        t.start();
    }
}

方法5 lambda 表达式创建 Runnable 子类对象

package Thread;

public class ThreadDemo5 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            //把服务用lambda表达式来描述
            //直接把lambda传给Thread构造方法
            System.out.println("hello");
        });
        t.start();
    }
}

1.4 多线程的优势-增加运行速度

可以观察多线程在一些场合下是可以提高程序的整体运行效率的。

使用 System.nanoTime() 可以记录当前系统的 纳秒 级时间戳.

serial 串行的完成一系列运算. concurrency 使用两个线程并行的完成同样的运算.

public class ThreadAdvantage {

   // 多线程并不一定就能提高速度,可以观察,count 不同,实际的运行效果也是不同的

   private static final long count = 10_0000_0000;

   public static void main(String[] args) throws InterruptedException {

       // 使用并发方式

       concurrency();

       // 使用串行方式

       serial();

  }

   private static void concurrency() throws InterruptedException {

       long begin = System.nanoTime();

       

       // 利用一个线程计算 a 的值

       Thread thread = new Thread(new Runnable() {

           @Override

           public void run() {

               int a = 0;

               for (long i = 0; i < count; i++) {

                   a--;

              }

          }

      });

       thread.start();

       // 主线程内计算 b 的值

       int b = 0;

       for (long i = 0; i < count; i++) {

           b--;

      }

       // 等待 thread 线程运行结束

       thread.join();

       

       // 统计耗时

       long end = System.nanoTime();

       double ms = (end - begin) * 1.0 / 1000 / 1000;

       System.out.printf("并发: %f 毫秒%n", ms);

  }

   private static void serial() {

       // 全部在主线程内计算 a、b 的值

       long begin = System.nanoTime();

       int a = 0;

       for (long i = 0; i < count; i++) {

           a--;

      }

       int b = 0;

       for (long i = 0; i < count; i++) {方法

说明

Thread()

创建线程对象

Thread(Runnable target)

使用 Runnable 对象创建线程对象

Thread(String name)

创建线程对象,并命名

Thread(Runnable target, String name)

使用 Runnable 对象创建线程对象,并命名

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值