生活中最常见的例子拆解——多线程(三)(小白也包可以看懂的!!1)

前言:

我们上一篇提到,这些概念的拆解:

①官方定义和大白话拆解对比;

②举生活中常见例子、图解辅助理解的形式



大家点一个 赞  或者  关注,我们一起进步!!!

1.5.1 单核CPU和多核CPU

  

单核CPU:

  • 一家咖啡店里,你是店里唯一的服务员。每当有顾客点单时,你必须按照他们到来的顺序一个接一个地为他们制作咖啡。即使有很多顾客同时进来,你也只能一次处理一个订单,因为只有你一个人。如果想让服务更快,要么你需要变得更熟练、更快地制作咖啡(比如通过培训提高技能),要么就需要增加更多的服务员(但这是单核CPU做不到的)。

多核CPU:
现在假设这家咖啡店有了不止一个服务员,比如说有两个或者更多。这样一来,当有多位顾客同时点单时,每个服务员可以同时为一位顾客制作咖啡。这样不仅可以让顾客更快得到他们的咖啡,而且整体的服务速度也大大提高了。这就是多核CPU的工作原理:有更多的“服务员”(即核心)可以同时处理多个任务,使得整个系统的性能得到了提升。

1.5.2 并行与并发

  •  并行:
  • 假如你有四双手,可以同时做四件不同的事情,比如一边剥豆子、一边洗碗、一边看书、一边看电视。因为你有四双手,所以你可以真的做到同时进行这些活动,而且不会影响到其他活动。这就是并行的概念:在同一时间做多件事情。

  • 并发:
  • 现在想象你只有一双手,但是你需要完成上面提到的四个任务。为了尽快完成所有任务,你可能会先剥一会儿豆子,然后去洗几分钟碗,接着看几页书,再看看电视。虽然你不能真正同时做这些事情,但你快速地在它们之间切换,使得整体看起来好像你在同一时间完成了所有任务。这就是并发的概念:在一个时间段内,快速轮流完成多个任务,让人感觉是同时进行的。

2.创建和启动线程

 

2.1概述:

  •  在Java中,我们可以通过使用java.lang.Thread类来创建和管理线程。线程就像是一个小小的工人,它可以在后台为你做事情,比如下载文件、处理数据等,而不会阻塞你正在使用的程序界面。当你想要你的程序同时做多件事情时,就可以创建多个这样的“小工人”——也就是线程。

2.2 方式1:继承Thread类

 

2.3 方式2:实现Runnable接口

详细拆解了俩种方法实现的步骤,加以代码实例作为解释。

上一篇的内容就讲了这些



我们接着继续!!!

3. Thread 类的常用结构 


3.1 构造器 

官方解释

1、public Thread()

  • 创建一个新的线程对象。这个新线程没有目标(即它不会执行任何任务),也没有指定名称,所以它的名字将由Java虚拟机自动生成。

2、public Thread(String name)

  • 创建一个带有指定名称的新线程对象。除了提供一个可读的名字外,这个构造器与无参构造器功能相同。

3、public Thread(Runnable target)

  • 创建一个新线程对象,该线程将执行作为参数传递的 Runnable 接口实现对象的任务。线程开始时会调用 Runnable 对象的 run 方法。

4、public Thread(Runnable target, String name)

  • 创建一个带有指定名称和 Runnable 目标的线程对象。线程启动时,它将执行 Runnable 实现对象中的 run 方法,并且线程将具有给定的名称。

大白话拆解:

1、创建一个空线程

  • 有一个新的工作空间,但还没有决定要在这个空间里做什么工作。你可以创建这个工作空间(线程),但是它现在是空的,不做任何事情。
Thread thread = new Thread();

2、创建一个有名字的空线程

  • 不仅有了一个工作空间,你还给它取了个名字,比如叫“我的第一个线程”。这有助于你在多个线程中识别它。
Thread namedThread = new Thread("我的第一个线程");

3、创建一个有工作任务的线程

  • 有一个具体的任务要完成,比如说打印出 "Hello World"。你可以把这个任务交给一个线程去做。为了做到这一点,你需要定义一个实现了 Runnable 接口的对象,然后把它交给线程。
Thread taskThread = new Thread(new MyTask());
// 假设 MyTask 是一个实现了 Runnable 接口的类

4、创建一个有工作任务并且有名字的线程

  • 这个就是上面两种情况的结合。你不仅给了线程一个任务,还给了它一个容易辨认的名字。
Thread namedTaskThread = new Thread(new MyTask(), "打印Hello World的线程");

举个栗子:

// 定义一个实现了 Runnable 接口的任务类
class PrintTask implements Runnable {
    public void run() {
        System.out.println("Hello from a thread!");
    }
}

public class ThreadExample {
    public static void main(String[] args) {
        // 使用不同的构造器创建线程

        // 1. 创建一个空线程
        Thread emptyThread = new Thread();
        emptyThread.start(); // 启动线程,但它不会做任何事

        // 2. 创建一个有名字的空线程
        Thread namedEmptyThread = new Thread("命名的空线程");
        namedEmptyThread.start(); // 启动线程,但它仍然不会做任何事

        // 3. 创建一个有任务的线程
        Thread taskThread = new Thread(new PrintTask());
        taskThread.start(); // 启动线程并执行任务

        // 4. 创建一个有任务并且有名字的线程
        Thread namedTaskThread = new Thread(new PrintTask(), "有任务的线程");
        namedTaskThread.start(); // 启动线程并执行任务
    }
}

代码解释和总结:

1. 定义任务类 PrintTask

class PrintTask implements Runnable {
    public void run() {
        System.out.println("Hello from a thread!");
    }
}
  • PrintTask 类:我们可以把它想象成一个包含具体工作的盒子。这个盒子里的工作就是打印一句话:“Hello from a thread!”。
  • implements Runnable:这表示我们的 PrintTask 盒子同意遵循一些规则,这些规则是由 Runnable 接口定义的。其中一个规则是必须有一个 run 方法,这个方法里包含了我们要执行的任务。
  • public void run():这是盒子内部的任务代码。当有人(或者计算机)打开这个盒子并说“开始工作吧”,它就会执行这里边的代码。在这个例子中,它会把那句话打印到屏幕上。

2、创建线程

Thread emptyThread = new Thread();
emptyThread.start(); // 启动线程,但它不会做任何事
  • new Thread():这里我们创建了一个新的线程,就像开启了一个新的工作空间,但是这个空间现在是空的,没有任何任务。
  • emptyThread.start():这行代码告诉线程“开始吧!”但是因为这个线程没有任务,所以它实际上什么也不做,只是简单地启动然后结束。

3、创建有名字的空线程:

Thread namedEmptyThread = new Thread("命名的空线程");
namedEmptyThread.start(); // 启动线程,但它仍然不会做任何事
  • new Thread("命名的空线程"):这次我们在创建线程时给它起了个名字——“命名的空线程”。这个名字可以帮助我们在程序中有多个线程时更容易识别它们。
  • namedEmptyThread.start():同样地,我们让这个线程开始工作。但由于它是空的,它也不会做任何事情。

4、创建有任务的线程

Thread taskThread = new Thread(new PrintTask());
taskThread.start(); // 启动线程并执行任务
  • new Thread(new PrintTask()):在这里,我们不仅创建了一个新的线程,还给了它一个任务去做。这个任务就是我们之前定义的 PrintTask 盒子里的内容。
  • taskThread.start():当我们调用 start() 方法时,线程就开始工作了,并且它会去执行 PrintTask 盒子里的任务,也就是打印出 “Hello from a thread!”。

5、创建有任务且有名字的线程

Thread namedTaskThread = new Thread(new PrintTask(), "有任务的线程");
namedTaskThread.start(); // 启动线程并执行任务
  • new Thread(new PrintTask(), "有任务的线程"):这一次,我们既给了线程一个任务(PrintTask),也给了它一个容易辨认的名字——“有任务的线程”。
  • namedTaskThread.start():和前面一样,调用 start() 方法后,线程开始工作,执行它的任务,同时我们知道它的名字,这样在程序中如果有多个线程,我们也能轻松找到它。

4. 多线程的生命周期

概述:

  • Java 语言使用Thread类及其子类的对象来表示线程,在它的一个完整的生命周 期中通常要经历如下一些状态:

4.1 JDK1.5 之前:(了解)

  • 5 种状态 线程的生命周期有五种状态:
  • 新建(New)、就绪(Runnable)、运行 (Running)、阻塞(Blocked)、死亡(Dead)。
  • CPU需要在多条线程之间切 换,于是线程状态会多次在运行、阻塞、就绪之间切换。

4.2 JDK1.5 及之后:6种状态

官方定义:


1、NEW(新建)

  • 线程刚被创建,但是并未启动。还没调用 start() 方法。


2、RUNNABLE(可运行)

  • 线程已经准备好运行,但是否正在运行取决于操作系统调度器。这个状态包括了就绪和运行两种情况。


3、TERMINATED(被终止)

  • 表明此线程已经结束生命周期,终止运行。


4、BLOCKED(锁阻塞)

  • 一个正在阻塞、等待一个监视器锁(锁对象)的线程处于这一状态。只有获得锁对象的线程才能有执行机会。


5、TIMED_WAITING(计时等待)

  • 一个正在限时等待另一个线程执行一个(唤醒)动作的线程处于这一状态。当前线程执行过程中遇到 Thread.sleep 或 join,Object.wait 并且在调用这些方法时设置了时间,那么当前线程会进入 TIMED_WAITING,直到时间到或被中断。


6、WAITING(无限等待)

  • 一个正在无限期等待另一个线程执行一个特别的(唤醒)动作的线程处于这一状态。当前线程执行过程中遇到 Object.wait,Thread.join,LockSupport.park 并且在调用这些方法时没有指定时间,那么当前线程会进入 WAITING 状态,直到被唤醒。

大白话拆解:

1、NEW(新建)

  • 这就像你刚刚买了一辆新车,但它还没有发动起来。你需要先启动它(调用 start() 方法),它才会开始工作。


2、RUNNABLE(可运行)

  • 这时候你的车已经发动起来了,随时准备上路。但是具体什么时候能开,要看交通灯(操作系统调度器)什么时候让你走。


3、TERMINATED(被终止)

  • 这表示你的车已经停好了,不再行驶了。线程已经完成了它的任务,结束了。


4、BLOCKED(锁阻塞)

  • 想象一下你在等红绿灯,但是前面的车挡住了你的去路。你只能等着前面的车让开(释放锁),你才能继续前进。


5、TIMED_WAITING(计时等待)

  • 这就像是你在等红绿灯,但是你知道这个红灯会在多久之后变绿。你可以设置一个时间,如果到了这个时间或者有人提前帮你变绿灯(中断),你就可以继续走了。


6、WAITING(无限等待)

  • 这就像是你在等红绿灯,但是你不知道这个红灯会持续多久。你需要等到有人帮你变绿灯(唤醒),你才能继续走。

图解:

举个栗子:

class PrintTask implements Runnable {
    public void run() {
        System.out.println("Hello from a thread!");
        try {
            Thread.sleep(5000); // 让线程进入 TIMED_WAITING 状态
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ThreadExample {
    public static void main(String[] args) throws InterruptedException {
        // 创建一个线程
        Thread taskThread = new Thread(new PrintTask(), "打印Hello World的线程");
        
        // 启动线程
        taskThread.start();
        
        // 等待一段时间后中断线程
        Thread.sleep(2000);
        taskThread.interrupt();
    }
}

代码解释和总结:

1. 定义任务类 PrintTask

class PrintTask implements Runnable {
    public void run() {
        System.out.println("Hello from a thread!");
        try {
            Thread.sleep(5000); // 让线程进入 TIMED_WAITING 状态
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  • PrintTask 类:想象一下这是一个小盒子,里面装着你要做的任务。这个盒子里的任务是打印一句话:“Hello from a thread!”。
  • implements Runnable:这表示我们的 PrintTask 盒子同意遵循一些规则,这些规则是由 Runnable 接口定义的。其中一个规则是必须有一个 run 方法,这个方法里包含了我们要执行的任务。
  • public void run():这是盒子内部的任务代码。当有人(或者计算机)打开这个盒子并说“开始工作吧”,它就会执行这里边的代码。
  • System.out.println("Hello from a thread!");:这行代码会让程序在屏幕上打印出那句话。
  • Thread.sleep(5000);:这行代码让当前线程暂停 5 秒钟(5000 毫秒)。在这期间,线程会进入 TIMED_WAITING 状态,就像你告诉线程:“休息一下,5 秒钟后再继续工作。”
  • catch (InterruptedException e):如果在等待期间有人打扰了线程(比如我们后面要讲到的中断),这个 catch 块会捕捉到这个打扰,并打印出错误信息,告诉我们发生了什么。

2、创建和·启动线程

public class ThreadExample {
    public static void main(String[] args) throws InterruptedException {
        // 创建一个线程
        Thread taskThread = new Thread(new PrintTask(), "打印Hello World的线程");
        
        // 启动线程
        taskThread.start();
  • Thread taskThread = new Thread(new PrintTask(), "打印Hello World的线程");:这里我们创建了一个新的线程,并给它分配了之前定义的任务(PrintTask),同时给这个线程起了个名字——“打印Hello World的线程”。这个名字可以帮助我们在程序中有多个线程时更容易识别它们。
  • taskThread.start();:这行代码告诉线程“开始吧!”线程开始后,它会去执行 PrintTask 盒子里的任务,也就是先打印那句话,然后休息 5 秒钟。

3、中断线程

        // 等待一段时间后中断线程
        Thread.sleep(2000);
        taskThread.interrupt();
    }
}
  • Thread.sleep(2000);:这行代码让主线程(即 main 方法所在的线程)暂停 2 秒钟。这是因为我们想在线程开始工作后稍等一会儿再打扰它。
  • taskThread.interrupt();:这行代码尝试中断 taskThread 线程。如果我们在这个时候打断线程,它会在 Thread.sleep(5000); 那里捕获到这个中断,并跳到 catch 块中处理。也就是说,线程不会等到 5 秒钟结束,而是提前被唤醒,并且会打印出一个错误信息,告诉我们它被中断了。



我们今天先到此为止,下次再见!!



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值