线程基本概念

 1. 线程是什么

一个线程就是一个 "执行流". 每个线程之间都可以按照顺讯执行自己的代码. 多个线程之间 "同时" 执行着多份代码.

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

2.为啥要有线程

  • 单核 CPU 的发展遇到了瓶颈. 要想提高算力, 就需要多核 CPU. 而并发编程能更充分利用多核 CPU资源.
  • 有些任务场景需要 "等待 IO", 为了让等待 IO 的时间能够去做一些其他的工作, 也需要用到并发编程.

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

  • 创建线程比创建进程更快.
  • 销毁线程比销毁进程更快.
  • 调度线程比调度进程更快.

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

 

3.进程和线程的区别

  • 进程是包含线程的. 每个进程至少有一个线程存在,即主线程。
  • 进程和进程之间不共享内存空间. 同一个进程的线程之间共享同一个内存空间.

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

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

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

线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对用户层提供了一些 API 供用户使用(例如 Linux 的 pthread 库).
Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进行了进一步的抽象和封装.

 

5.第一个多线程程序

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

  • 每个线程都是一个独立的执行流
  • 多个线程之间是 "并发" 执行的
package thread;

class MyThread2 extends Thread {
    @Override
    public void run() {
        while (true) {
            System.out.println("hello thread");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class Test2 {
    public static void main(String[] args) throws InterruptedException {
        MyThread2 myThread2 = new MyThread2();
        myThread2.start();

        while (true) {
            System.out.println("hello main");
            Thread.sleep(2000);
        }
    }
}

运行结果:

 

6.使用 jconsole 命令观察线程

在jdk的bin目录里面有jconsole.exe可执行程序!


 

 

7.创建线程

方法1 继承 Thread 类

package thread;

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("hello");

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

public class Test1 extends Thread {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
}


方法2 实现 Runnable 接口

package thread;

class MyRunnable implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("hello");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

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


其他变形方法

  • 匿名内部类创建 Thread 子类对象
package thread;

public class Test4 {
    public static void main(String[] args) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        thread.start();
    }
}
  • 匿名内部类创建 Runnable 子类对象
package thread;

public class Test5  {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    System.out.println("hello");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        thread.start();
    }
}
  •  lambda 表达式创建 Runnable 子类对象
package thread;

public class Test6 {
    public static void main(String[] args) {
        Thread thread1 = new Thread(()-> {
            while (true){
                System.out.println("hello thread1");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        thread1.start();

        Thread thread2 = new Thread(()->{
           while (true){
               System.out.println("hello thread2");
               try {
                   Thread.sleep(2000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
        });
        thread2.start();
    }
}

thread.start() 和 thread.run() 的区别

package thread;

public class Test7 {
    public static void main(String[] args) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        thread.run();

        while (true){
            System.out.println("hello main");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

运行结果:

可以看到这个代码只是在执行打印hello thread,没有打印hello main

当前run只是一个普通方法的调用,此处没有创建出新的线程

当前代码只有一个main这样的线程 

得是main线程执行完第一个循环,才能执行后续的循环代码

第一个代码是死循环,永远也执行不了第二个循环了

start这个方法,创建了新线程,新线程中执行run方法

run这个方法自身不具备创建线程的能力,仍然在旧的线程中执行的

必须得thread.start()才能创建线程。

8.多线程的优势

增加运行速度

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

package thread;

public class Test8 {
    //串行执行
    private static void serial() {
        long beg = System.currentTimeMillis();
        long a = 0;
        for (long i = 0; i < 10_0000_0000; i++) {
            a++;
        }
        long b = 0;
        for (long i = 0; i < 10_0000_0000; i++) {
            b++;
        }
        long end = System.currentTimeMillis();
        System.out.println("消耗时间:" + (end - beg) + " ms");
    }

    //并发执行
    public static void main(String[] args) {
        serial();
    }
}


引入多线程:

package thread;

public class Test8 {
    //串行执行
    private static void serial() {
        long beg = System.currentTimeMillis();
        long a = 0;
        for (long i = 0; i < 10_0000_0000; i++) {
            a++;
        }
        long b = 0;
        for (long i = 0; i < 10_0000_0000; i++) {
            b++;
        }
        long end = System.currentTimeMillis();
        System.out.println("消耗时间:" + (end - beg) + " ms");
    }

    public static void concurrency() throws InterruptedException {
        long beg = System.currentTimeMillis();
        Thread thread = new Thread(() -> {
            long a = 0;
            for (long i = 0; i < 10_0000_0000; i++) {
                a++;
            }
        });
        thread.start();
        Thread thread2 = new Thread(() -> {
            long b = 0;
            for (long i = 0; i < 10_0000_0000; i++) {
                b++;
            }
        });
        thread2.start();
        
        //join的效果就是等待线程结束. thread.join意思就是等到thread执行完了,才会返回,继续往下走.
        //等上面的两个线程执行完,再进行计时
        thread.join();
        thread2.join();

        long end = System.currentTimeMillis();
        System.out.println("消耗时间:" + (end - beg) + " ms");
    }

    //并发执行
    public static void main(String[] args) throws InterruptedException {
        //  serial();
        concurrency();
    }
}

 可以明显看到运行速度的提升!

使用场景:

在实际开发中,有的时候,如果执行任务时间太长了,就可以考虑能否使用多线程进行任务拆分,提高运行速度!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
进程和线程是操作系统中的两个基本概念,它们都是用来执行程序的执行单元,但在一些方面有着明显的区别。 1. 进程(Process): - 进程是程序在执行过程中的一个实例。 - 每个进程都有自己的独立内存空间,包括代码段、数据段和堆栈段。 - 进程之间相互独立,拥有各自的资源,通信需要通过进程间通信(IPC)机制。 - 进程拥有自己的进程控制块(PCB),用于描述进程的状态、资源和调度信息。 2. 线程(Thread): - 线程是进程中的一个执行单元。 - 多个线程可以共享同一个进程的内存空间,包括代码段、数据段和堆栈段。 - 线程之间共享进程的资源,如打开的文件、信号处理等。 - 线程线程控制块(TCB)来描述,每个线程有自己的栈和寄存器上下文。 区别: 1. 资源占用:每个进程都有独立的内存空间和系统资源,而线程共享进程的资源。 2. 创建销毁开销:创建或销毁进程比线程开销大,因为进程需要分配独立的内存空间和系统资源,而线程只需要创建线程控制块。 3. 切换开销:进程切换的开销较大,需要保存和恢复整个进程的上下文,而线程切换只需要保存和恢复线程的上下文。 4. 通信和同步:进程间通信需要使用进程间通信机制,如管道、消息队列等。线程间通信和同步相对容易,可以使用共享内存、信号量、互斥量等机制。 总结: 进程和线程都是用于执行程序的执行单元,但进程是资源分配的基本单位,线程是CPU调度的基本单位。多线程比多进程更轻量级,线程之间的切换开销更小,但进程之间相互独立,安全性更高。在实际应用中,需要根据具体需求选择使用进程还是线程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿瞒有我良计15

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值