进程与多线程

什么是多线程?

线程是操作系统进行时间片分配调度的最小单位
进程是操作系统资源分配的最小单位

在每个进程中,至少都有一个线程(主线程)
在每个进程中,也允许拥有多个线程


创建线程

public class 手动创建线程 {
    static class MyThread extends Thread {
        @Override
        public void run() {
        }
    }

    /*
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();

        // t1 和 t2 这两个引用指向了各自的线程对象
        // 线程最终要执行是 run 方法中的代码
    }
    */

    static class MyRunnable implements Runnable {
        @Override
        public void run() {
        }
    }

    public static void main(String[] args) {
        MyRunnable mr1 = new MyRunnable();
        MyRunnable mr2 = new MyRunnable();
        // mr1 和 mr2 引用指向各自的 任务对象

        // 最终线程需要有线程对象
        Thread t1 = new Thread(mr1);    // mr1 指向的任务作为 t1 线程的任务
        Thread t2 = new Thread(mr1);    // mr1 指向的任务作为 t2 线程的任务
        Thread t3 = new Thread(mr2);    // mr2 指向的任务作为 t3 线程的任务
    }
}

让线程运行就有两种方法:

  • 创建好线程后,p.start(); 就会执行线程里run的代码
PrimeThread p = new PrimeThread(143);
     p.start();
  • 创建好线程后再继承Runnable的run方法,创建出一个任务,然后把任务交给线程
PrimeRun p = new PrimeRun(143);
     new Thread(p).start();

多线程情况下可能会提升速度,但一味的增加线程是不会一直提升速度的。因为抢到的CPU是有极限的,线程越多,耗费掉的创建/销毁的时间成本就越高,OS负担会变大,会降低速度。

比如单线程时间是6s, 10个线程是 2s,为什么不是原来的十分之一?
两种情况:排队的除了此线程外还有其他线程,线程本身创建/销毁也是需要时间


在某些场景下必须使用多线程,最常见的就是使用阻塞式IO的场景

Scanner scanner = new Scanner(System.in);
scanner.nextLine();

这个方法调用返回时间是无法预期的,所以什么时候用户输入了才会返回

举个例子:不断的读取用户给的数字,计算这个数字的斐波那契数,要求即使上一个数字还在计算中,也不耽误下一个数字的计算

情况一:不使用多线程

public class 线程的优势_提升速度 {
    private static final long COUNT = 1000000000L;
    private static final int N = 10;                // 一共计算 10 次

    private static void calc() {
        long r = 0;

        for (long i = 0; i < COUNT; i++) {
            r += i;
        }

        System.out.println(r);
    }

    private static class CalcThread extends Thread {
        @Override
        public void run() {
            // 一个线程只需要计算一遍 calc
            calc();
        }
    }
    public static void main(String[] args) throws InterruptedException {
            long start = System.nanoTime();
        for (int i = 0; i < N; i++) {
            calc();
        }
        long end = System.nanoTime();
        double 耗时ms = (end - start) * 1.0 / 1000 / 1000;
        System.out.println("耗时: " + 耗时ms);
    }
}

结果如下:在这里插入图片描述

情况二:使用多线程

public class 线程的优势_提升速度 {
    private static final long COUNT = 1000000000L;
    private static final int N = 10;                // 一共计算 10 次

    private static void calc() {
        long r = 0;

        for (long i = 0; i < COUNT; i++) {
            r += i;
        }

        System.out.println(r);
    }

    private static class CalcThread extends Thread {
        @Override
        public void run() {
            // 一个线程只需要计算一遍 calc
            calc();
        }
    }

    // 主线程一个线程在计算
    public static void main(String[] args) throws InterruptedException {
        long start = System.nanoTime();

        CalcThread[] threads = new CalcThread[N - 1];
        // N 个线程同时执行,其实主线程不需要我创建,所以我需要创建 N - 1 个线程
        for (int i = 0; i < N - 1; i++) {
            // 创建了 N - 1 个线程,每个线程都执行一次
            CalcThread thread = new CalcThread();
            thread.start();
            threads[i] = thread;
        }

        calc(); // 剩余的一次,放到主线程中来计算

        // 什么时候记录结束时间?
        // 代码走到的这里时,只代表主线程中的 calc 计算完了,但
        // 另外的 N - 1 个线程是否计算完成,是不知道的
        // 所以,我需要等另外 N - 1 个线程全部完成,才结束计时
        for (int i = 0; i < N - 1; i++) {
            threads[i].join(); // 阻塞在这个方法上,直到 threads[i] 线程执行结束
        }

        // 这时代表所有的线程都执行结束了
        long end = System.nanoTime();
        double 耗时ms = (end - start) * 1.0 / 1000 / 1000;
        System.out.println("耗时: " + 耗时ms);
   }
}

在这里插入图片描述
所以在数据计算较多的情况下,使用多线程可以降低时间。


start()方法

start就是启动了一个线程
给了抢CPU的资格,但是并没有真正给它CPU使用资格

所以当前CPU的线程被调度下来,JVM在选择合适的线程分配CPU时,选中了t,把t调度到CPU上,才会运行

在这里插入图片描述


Thread.currentThread()

这个静态方法可以返回当前线程的引用
这个方法在哪个线程中被调用,返回的就是哪个线程的引用

public class 查看一个线程的主要属性 {
    private static void 打印当前线程的属性() {
        Thread thread = Thread.currentThread();

        long id = thread.getId();
        System.out.println(id + ": 线程的唯一标识: " + id);
        String name = thread.getName();
        System.out.println(id + ": 线程的名字: " + name);
        int priority = thread.getPriority();
        System.out.println(id + ": 线程的优先级: " + priority);
        Thread.State state = thread.getState();
        System.out.println(id + ": 线程的当前状态: " + state);
        boolean alive = thread.isAlive();
        System.out.println(id + ": 线程是否还活着: " + alive);
        boolean interrupted = thread.isInterrupted();
        System.out.println(id + ": 线程是否被通知停止: " + interrupted);
        boolean daemon = thread.isDaemon();
        System.out.println(id + ": 线程是否是后台线程: " + daemon);
    }
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            打印当前线程的属性();
        }, "张三");
        t1.start();
        // 主线程还在 CPU 上呢(根据我的现象)

        Thread t2 = new Thread(() -> {
            打印当前线程的属性();
        }, "李四");
        t2.start();
        // 主线程还在 CPU 上呢(根据我的现象)

        打印当前线程的属性();
    }
}

此代码经过多次运行,得到不同的结果
在这里插入图片描述
相同的代码出现不同的现象是因为线程在CPU执行时,什么时候被切换下来时随机的,线程在争夺CPU时,哪个线程挣到的CPU是随机的

乱序只在线程和线程之间,线程内部不是乱序是有序的


t.join()

等待一个线程(t)完全停止

意味着t要分配CPU了,直到t线程完全结束,调用线程才会重新拥有抢CPU的资格

当执行到 t.join() 时 就不会再往下执行了,直到t结束,下面的语句才会执行到。


总结

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值