首页 > 其他分享 >11 多线程详解

11 多线程详解

时间:2025-01-15 23:43:34浏览次数:1  
标签:11 Runnable run Thread 详解 线程 多线程 方法 public

线程简介

程序:程序就像一堆写好的指令和数据放在一起,它是静止的,不会自己动起来。

进程(Process):进程是把程序真正运行起来的过程,它是动态的,系统会给它分配各种资源,比如内存等。

线程(Thread)一个进程里通常会有好几个线程,最少也得有一个,不然进程就没啥用了。线程是 CPU 安排干活和实际执行任务的小单位

注意事项:很多时候说的多线程其实是模拟出来的,真正的多线程得有多个 CPU(就是多核,像服务器那种)。要是模拟的多线程,只有一个 CPU 的时候,同一瞬间 CPU 只能执行一个代码,不过因为它切换得特别快,就好像是同时在执行好多事一样,但其实是一种错觉。

  • 线程就像独立的做事通道。
  • 程序运行时,就算你不创建,后台也有主线程、gc 线程等好多线程。
  • main () 是主线程,是程序开始运行的地方,负责执行整个程序。
  • 一个进程里开了多个线程,啥时候运行由调度器安排,调度器和操作系统关系密切,顺序不能随便改。
  • 多个线程操作同一份资源会抢资源,得用并发控制。
  • 用线程会有额外花费,像 CPU 安排线程的时间和并发控制的花费。
  • 每个线程在自己的工作内存里交流,内存管不好会让数据乱套。

线程实现(重点)

  • 线程创建

继承Thread类

  1. 自定义线程类继承Thread
  2. 重写run()方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动线程
public class MyThread extends Thread {
    // 重写Thread类的run方法,run方法里是线程要执行的任务
    @Override
    public void run() {
        // 这里写线程要执行的具体任务
        for (int i = 0; i < 20; i++) {
            System.out.println("自定义线程1正在运行:" + i);
        }
    }
}

public class ThreadExample {
    public static void main(String[] args) {
        // 创建自定义线程的对象
        MyThread myThread = new MyThread();
        // 启动线程,注意不是调用run方法,而是调用start方法,调用start方法后会自动调用run方法
        myThread.start();
    }
}

首先,我们定义了一个 MyThread 类,它继承自 Thread 类。在 MyThread 类中,重写了 run 方法。run 方法是线程的执行体,当线程启动后,会自动调用这个方法执行具体的任务。

run 方法中,我们使用了一个 for 循环打印了一些信息,这就是这个线程要执行的具体任务。

然后,在 ThreadExample 类的 main 方法中,创建了 MyThread 类的对象 myThread

最后,调用 myThread.start() 方法来启动线程。这里要注意的是,不能直接调用 run 方法,因为直接调用 run 方法只是普通的方法调用,而不会开启新的线程,只有调用 start 方法,Java 虚拟机才会为这个线程分配资源并启动线程,进而调用 run 方法。

实现Runnable接口(推荐使用)

  1. 定义MyRunnable类实现Runnable接口
  2. 实现run()方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动线程
public class MyRunnable implements Runnable {
    // 实现 Runnable 接口的 run 方法,该方法包含线程要执行的任务
    @Override
    public void run() {
        // 这里是线程要执行的具体任务
        for (int i = 0; i < 10; i++) {
            System.out.println("自定义线程正在运行:" + i);
        }
    }
}


public class RunnableExample {
    public static void main(String[] args) {
        // 创建 MyRunnable 类的实例
        MyRunnable myRunnable = new MyRunnable();
        // 创建 Thread 对象,并将 MyRunnable 实例作为参数传递给它
        Thread thread = new Thread(myRunnable);
        // 启动线程
        thread.start();
    }
}

首先,我们创建了一个名为 MyRunnable 的类,它实现了 Runnable 接口。在 MyRunnable 类中,重写了 run 方法。run 方法是 Runnable 接口中唯一的抽象方法,当线程启动后,它将执行 run 方法中所包含的任务。

run 方法中,我们使用了一个 for 循环,从 0 到 9 打印信息,这就是该线程要执行的具体任务。

然后,在 RunnableExample 类的 main 方法中,创建了 MyRunnable 类的实例 myRunnable

接着,我们创建了一个 Thread 对象 thread,并将 myRunnable 作为参数传递给它。这是因为 Thread 类的构造函数可以接受一个 Runnable 接口的实例,这样 thread 线程启动后,会执行 myRunnablerun 方法。

最后,调用 thread.start() 方法启动线程。

这种实现 Runnable 接口的方式是 Java 中创建线程的一种常用方式,与继承 Thread 类相比,实现 Runnable 接口的优势在于避免了 Java 单继承的限制,使代码更加灵活,因为一个类可以实现多个接口,但只能继承一个父类。

另外,使用 Runnable 接口可以方便地将任务和线程对象分离,在需要将任务共享给多个线程时,只需要创建多个 Thread 对象,并将同一个 Runnable 实例传递给它们,就可以让多个线程执行相同的任务。例如,如果我们想让多个线程执行相同的 run 方法,可以这样做:

public class RunnableExample {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread1 = new Thread(myRunnable);
        Thread thread2 = new Thread(myRunnable);
        thread1.start();
        thread2.start();
    }
}

上述代码创建了两个 Thread 对象 thread1thread2,它们都执行 myRunnablerun 方法,实现了多个线程执行相同任务的目的。

实现Callable接口(了解即可)

  1. 实现Callable接口,需要返回值类型
  2. 重写call方法,需要抛出异常
  3. 创建目标对象
  4. 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
  5. 提交执行:Future result1 = ser.submit(t1);
  6. 获取结果:boolean r1 = result.get()
  7. 关闭服务:ser.shutdownNow()
  • Lamda 表达式

Lambda 表达式是 Java 8 引入的一个新特性,它可以让你用一种简洁的方式表示一个匿名函数。就像是一种简洁的代码书写方式,让你能更方便地编写一些功能简单的函数,而不用像以前那样,为了一个小功能去创建一个完整的类或方法。

通常的格式(参数列表) -> {函数体}。比如说,如果你想定义一个对两个整数求和的函数,传统的写法可能要写一个完整的方法,但是用 Lambda 表达式可以写成 (int a, int b) -> {return a + b;}

Lambda 表达式的优点

  • 简洁性:能大大减少代码量,让代码看起来更简洁,读起来也更清晰,尤其是对于那些功能比较简单的函数,以前可能需要写好几行,现在用 Lambda 就能在一行里搞定。
  • 功能性:可以作为参数传递给其他方法,在使用 Java 的一些函数式接口(只有一个抽象方法的接口)时特别有用。比如 Java 中的 Runnable 接口,以前你可能需要创建一个类来实现它,现在可以直接使用 Lambda 表达式。

例如,使用 Lambda 表达式创建一个 Runnable 线程,传统写法是:

// 匿名内部类,没有类的名称,必须借助接口或者父类。原本Runnable接口使用MyRunnable类来实现。
Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("线程正在运行");
    }
};

使用 Lambda 表达式后可以写成:

Runnable runnable = () -> {
    System.out.println("线程正在运行");
};

下面是lambda大致推导过程(供参考):

public class LambdaDemo {

    // 3.静态内部类:static修饰,类里面定义类
    static class Like2 implements ILike{
        @Override
        public void lambda() {
            System.out.println("i like lambda2");
        }
    }


    public static void main(String[] args) {
        ILike like = new Like();
        like.lambda();

        like = new Like2();
        like.lambda();

        // 4.局部内部类:方法里面定义类
        class Like3 implements ILike{
            @Override
            public void lambda() {
                System.out.println("i like lambda3");
            }
        }

        like = new Like3();
        like.lambda();

        // 5.匿名内部类:没有类的名称,必须借助接口或者父类
        like = new ILike() {
            @Override
            public void lambda() {
                System.out.println("i like lambda4");
            }
        };
        like.lambda();


        // 6.用 lambda 简化:接口和方法都不要了,只留(参数列表) -> {函数体}
        like = () -> {System.out.println("i like lambda5");};
        like.lambda();
    }
}


// 1.定义一个函数式接口:一种特殊接口,只包含一个抽象方法
interface ILike{
    void lambda();
}

// 2.类实现接口
class Like implements ILike{
    @Override
    public void lambda() {
        System.out.println("i like lambda");
    }
}

线程状态


线程同步(重点)


线程通信问题


高级主题


标签:11,Runnable,run,Thread,详解,线程,多线程,方法,public
From: https://www.cnblogs.com/you-ni/p/18673917

相关文章

  • 线性回归超详解
    目录一、回归问题vs分类问题二、线性回归1、一句话理解2、数学推导2.1线性函数表示  2.2损失函数2.3梯度下降2.3.1什么是梯度2.3.2梯度下降目标 2.3.3过程2.3.4迭代公式3、特征预处理3.1为什么要预处理3.2数据归一化方法1)最小-最大归一化2)Z-Scor......
  • 【详解】HadoopHDFS操作实例
    目录HadoopHDFS操作实例环境准备HDFS基本命令1.查看HDFS目录内容2.创建目录3.上传文件4.下载文件5.删除文件或目录6.查看文件内容高级操作1.文件重命名2.设置文件权限3.查看文件系统状态1.创建目录2.上传文件3.下载文件4.删除文件或目录注意事......
  • H3CNE-11-生成树协议STP
    STP:SpanningTreeProtocol,可以在提高可靠性的同时又能避免环路带来的各种问题。一句话总结STP的作用:防止交换机环路。为了提高网络的可靠性,交换网络中通常会使用冗余链路,然而冗余链路会给交换网络带来环路风险,并导致广播风暴以及MAC地址表不稳定的问题,进而会影响到用户的......
  • P11 ABC122D We Like AGC
    ​ 终于淦死了这题...​ 还是有点烦的,最后没想到直接爆力DFS记忆化搜索就完事了...​ 主要是搜索的状态设置,因为它说交换相邻两个字母后不能出现\(AGC\),所以考虑的字符串长度应该为四,因此直接设置最后四个字母保留在搜索中。constintN=105,mod=1e9+7;lln,f[N][5][5][......
  • 【multisim让七段显示器连续显示奇数或偶数生成与合成脉冲】2022-6-11
    缘由multisim如何让七段显示器连续显示奇数-其他-CSDN问答 脉冲延时合成极性选择......
  • P3514 [POI2011] LIZ-Lollipop
    题意:给你一个字符串,'T'代表2,'W'代表1。\(m\)次询问,每次问你有没有一个区间和等于\(x\),有则输出一个区间,否则输出"NIE"。我们观察只给1和2这两个值有什么用,如果我们知道\(x\)是有的,并且区间为\(l_x\)和\(r_x\),那么如果\(s[l_x]\)或者\(s[r_x]\)为2,是不是能推出\(x-2\),否则两......
  • 逐笔成交逐笔委托Level2高频数据下载和分析:20250115
    逐笔成交逐笔委托下载链接:https://pan.baidu.com/s/1uRCmUTFoUZShauQ0gJYFiw?pwd=f837提取码:f837--------------------Level2逐笔成交逐笔委托数据分享下载 采用Level2逐笔成交与逐笔委托的详细记录,这种毫秒级别的数据能揭露众多关键信息,如庄家意图、虚假交易,使所有......
  • Wgpu图文详解(05)纹理与绑定组
    前言什么是纹理?纹理是图形渲染中用于增强几何图形视觉效果的一种资源。它是一个二维或三维的数据数组,通常包含颜色信息,但也可以包含其他类型的数据,如法线、高度、环境光遮蔽等。纹理的主要目的是为几何图形的表面提供详细的视觉效果,使其看起来更加真实和复杂。而我们常见的图片......
  • 使用拓扑键实现拓扑感知的流量路由和CPU拓扑感知调度 Cilium 1.11 发布,带来内核级服务
    https://kubernetes.io/zh-cn/docs/concepts/services-networking/topology-aware-routing/https://help.aliyun.com/zh/ack/ack-managed-and-ack-dedicated/user-guide/topology-aware-cpu-schedulinghttps://kubernetes.p2hp.com/docs/concepts/services-networking/servi......
  • U-Boot启动流程详解
    一、第一部分要分析uboot的启动流程,首先要找到“入口”,找到第一行程序在哪里。程序的链接是由链接脚本来决定的,所以通过链接脚本可以找到程序的入口。打开u-boot.lds文件看到第三行,可以发现_start是代码的入口点。ENTRY(_start)_start在文件arch/arm/lib/vectors.S中有......