Java EE初阶启程记02---认识线程 - 实践

 个人主页:寻星探路

作者简介:Java研发方向学习者

个人专栏:、《

⭐️人生格言:没有人生来就会编程,但我生来倔强!!!



目录

1、概念

1.1线程是什么

1.2为什么要有线程

1.3进程和线程的区别

1.4Java的线程和操作系统线程的关系

2、第一个多线程程序

3、创建线程

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


1、概念

1.1线程是什么

        ⼀个线程就是⼀个"执行流",每个线程之间都可以按照顺序执行自己的代码。多个线程之间“同时”执行着多份代码。      

        还是回到我们之前的银行的例子中。之前我们主要描述的是个人业务,即一个人完全处理自己的业务。我们进一步设想如下场景:

        ⼀家公司要去银行办理业务,既要进行财务转账,⼜要进行福利发放,还得进行缴社保。

        如果只有张三一个会计就会忙不过来,耗费的时间特别长。为了让业务更快的办理好,张三又找来两位同事李四、王五一起来帮助他,三个人分别负责一个事情,分别申请一个号码进行排队,自此就有了三个执行流共同完成任务,但本质上他们都是为了办理一家公司的业务。

        此时,我们就把这种情况称为多线程,将⼀个大任务分解成不同小任务,交给不同执行流就分别排队执行。其中李四、王五都是张三叫来的,所以张三一般被称为主线程(MainThread)。

1.2为什么要有线程

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

        单核CPU的发展遇到了瓶颈,要想提高算力,就需要多核CPU,而并发编程能更充分利用多核CPU资源

        有些任务场景需要“等待IO”,为了让等待IO的时间能够去做⼀些其他的工作,也需要用到并发编程。

其次,虽然多进程也能实现并发编程,但是线程比进程更轻量

        创建线程比创建进程更快

        销毁线程比销毁进程更快

        调度线程比调度进程更快

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

1.3进程和线程的区别

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

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

        比如之前的多进程例子中,每个客户来银行办理各自的业务,但他们之间的票据肯定是不想让别人知道的,否则钱不就被其他人取走了么。而上面我们的公司业务中,张三、李四、王五虽然是不同的执行流,但因为办理的都是一家公司的业务,所以票据是共享着的。这个就是多线程和多进程的最大区别。

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

        一个进程挂了一般不会影响到其他进程,但是一个线程挂了,可能把同进程内的其他线程一起带走(整个进程崩溃)。

1.4Java的线程和操作系统线程的关系

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

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

2、第一个多线程程序

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

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

        多个线程之间是“并发”执行的

 import java.util.Random;
 public class ThreadDemo {
    private static class MyThread extends Thread {
        @Override
        public void run() {
            Random random = new Random();
            while (true) {
                // 打印线程名称
                System.out.println(Thread.currentThread().getName());
                try {
                    // 随机停⽌运⾏ 0-9 秒
                    Thread.sleep(random.nextInt(10));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.start();
        Random random = new Random();
        while (true) {
            // 打印线程名称
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(random.nextInt(10));
            } catch (InterruptedException e) {
                 // 随机停⽌运⾏ 0-9 秒
                 e.printStackTrace();
            }
        }
    }
 }

使用 jconsole 命令观察线程

3、创建线程

方法1继承Thread类

        继承Thread来创建一个线程类

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("这⾥是线程运⾏的代码");
    }
}

        创建MyThread类的实例

MyThread t = new MyThread();

        调用start方法启动线程

t.start();        // 线程开始运⾏

方法2实现Runnable接口

1)实现Runnable接口

 class MyRunnable implements Runnable {
     @Override
     public void run() {
         System.out.println("这⾥是线程运⾏的代码");
     }
 }

2)创建Thread类实例,调用Thread的构造方法时将Runnable对象作为target参数

Thread t = new Thread(new MyRunnable());

3)调用start方法

t.start();        // 线程开始运⾏

对比上面两种方法:

        继承Thread类,直接使用this就表示当前线程对象的引用

        实现Runnable接口,this表示的是MyRunnable的引用,需要使用Thread.currentThread()

其他变形

        匿名内部类创建Thread子类对象

// 使⽤匿名类创建Thread ⼦类对象
Thread t1 = new Thread() {
    @Override
    public void run() {
        System.out.println("使⽤匿名类创建Thread⼦类对象");
    }
};

        匿名内部类创建Runnable子类对象

// 使⽤匿名类创建Runnable⼦类对象
Thread t2 = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("使⽤匿名类创建 Runnable ⼦类对象");
    }
});

        lambda表达式创建Runnable子类对象

// 使⽤ lambda 表达式创建 Runnable ⼦类对象
Thread t3 = new Thread(() -> System.out.println("使⽤匿名类创建 Thread 子类对象"));
Thread t4 = new Thread(() -> {
    System.out.println("使⽤匿名类创建 Thread ⼦类对象");
});

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++) {
            b--;
        }
        long end = System.nanoTime();
        double ms = (end - begin) * 1.0 / 1000 / 1000;
        System.out.printf("串⾏: %f 毫秒%n", ms);
    }
 }

并发:399.651856毫秒

串行:720.616911毫秒

posted on 2025-10-23 10:04  ljbguanli  阅读(0)  评论(0)    收藏  举报