JavaSE08:详解多线程(超详细)

写在前面

经过两天的学习,超梦终于将多线程的基础知识学了一遍,笔记做完之后,赶紧写篇博客再巩固一下。为了保证效果,博客也会像笔记一样的详细~(部分截图来自尚硅谷宋红康老师课件)

程序、进程、线程

程序:为了实现某个功能,用某种语言的一组指令组成的静态代码。
进程:正在运行中的程序。
线程:进程可进一步细化为线程,是一个程序内部的一条执行路径。
多线程:若一个进程同一时间并行执行多个线程,就是支持多线程的。

在这里插入图片描述

单核CPU与多核CPU

单核CPU:同一时间内只能执行一条线程。表现为“多线程”是假的,主要通过切换CPU对进程控制,切换速度过快让我们误认为是多线程。
多核CPU:真正可以执行多线程。

并行与并发

并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事。

使用多线程的优点

1. 提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
2. 提高计算机系统CPU的利用率
3. 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改

何时需要多线程

1. 程序需要同时执行两个或多个任务。
2. 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
3. 需要一些后台运行的程序时。

线程的创建和使用(重点)

这里主要介绍JDK5.0之前的两种创建方式

关于Thread类

介绍

在这里插入图片描述

构造器

1.Thread():创建新的Thread对象
2.Thread(String threadname):创建线程并指定线程实例名
3.Thread(Runnable target):指定创建线程的目标对象,它实现了Runnable接口中的run方法
4.Thread(Runnable target, String name):创建新的Thread对象

常用方法

在这里插入图片描述

在这里插入图片描述

这些方法将会在后面的内容带大家慢慢熟悉,接下来进入重点:创建线程的两种方式!

方式一:继承Thread类创建线程

步骤:

  1. 定义子类继承Thread类。
  2. 子类中重写Thread类中的run方法。
  3. 创建Thread子类对象,即创建了线程对象。
  4. 调用线程对象start方法:启动线程,调用run方法。
代码示例
package com.deserts.demo01;


//1) 定义子类继承Thread类。
class Thread1 extends Thread {
   
    // 2) 子类中重写Thread类中的run方法。
    @Override
    public void run() {
   
        for (int i = 0; i < 30; i++) {
   
            if (i % 2 == 0) {
   
                System.out.println(i);
            }
        }
    }
}

class Thread2 extends Thread {
   
    @Override
    public void run() {
   
        for (int i = 0; i < 30; i++) {
   
            if (i % 2 != 0) {
   
                System.out.println(i);
            }
        }
    }
}

public class Thread01 {
   
    public static void main(String[] args) {
   
        // 3) 创建Thread子类对象,即创建了线程对象。
        Thread1 t1 = new Thread1();
        Thread2 t2 = new Thread2();
        // 4) 调用线程对象start方法:启动线程,调用run方法。
        t1.start();
        t2.start();
    }
}


运行结果:

在这里插入图片描述

我们也可以采用匿名内部类的方式
package com.deserts.demo01;

public class ThreadTest01 {
   
    public static void main(String[] args) {
   
        //创建匿名内部类的方式
        new Thread() {
   
            @Override
            public void run() {
   
                for (int i = 0; i < 30; i++) {
   
                    if (i % 2 == 0) {
   
                        System.out.println(i);
                    }
                }
            }
        }.start();
    }
}

运行结果:
在这里插入图片描述

创建过程的注意点:
  1. 如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程模式。
  2. run()方法由JVM调用,什么时候调用,执行的过程控制都有操作系统的CPU调度决定。
  3. 想要启动多线程,必须调用start方法。
  4. 一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上的异常“IllegalThreadStateException”。

方式二:实现Runnable接口的方式

步骤:

  1. 定义子类,实现Runnable接口。
  2. 子类中重写Runnable接口中的run方法。
  3. 通过Thread类含参构造器创建线程对象。
  4. 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
  5. 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。

代码实现:

package com.deserts.demo01;

//1) 定义子类,实现Runnable接口。
class Thread02 implements Runnable {
   

    //2) 子类中重写Runnable接口中的run方法。
    @Override
    public void run() {
   
        for (int i = 0; i < 30; i++) {
   
            if (i % 2 == 0) {
   
                System.out.println(i);
            }
        }
    }
}


public class ThreadTest02 {
   
    public static void main(String[] args) {
   
        //4) 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
        Thread02 t2 = new Thread02();
        //3) 通过Thread类含参构造器创建线程对象。
        Thread t1 = new Thread(t2);
        //5) 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。
        t1.start();
    }

}


运行结果:

在这里插入图片描述

关于为什么要将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中,我们可以看看源码:

在这里插入图片描述在这里插入图片描述这样我们可以知道:Runnable接口的子类实现了Runnable接口的run抽象方法,在传入参数时涉及到了多态,如:Runnable target = new Thread02(),Thread02是实现Runnable接口的子类,使用有参构造器创建Thread对象后调用start()方法时会调用run()方法,此时的run()方法已经Thread02重写过了!

两种方法的对比

在这里插入图片描述

线程的优先级

在这里插入图片描述

线程的分类

在这里插入图片描述

线程的生命周期

基本概念

在这里插入图片描述

过程图解

在这里插入图片描述

线程的同步(重点)

为什么要有线程的同步?

举个例子:你的银行账户有3000元,你和你的对象同时在不同地点都取出2000元,银行会先判断你是否余额充足,余额足了再扣钱,但是银行总不可能让你们两个人都·取了吧?这样岂不是亏了2000块!这样的问题可以理解为线程不安全,因为都操作了共享数据!

在这里插入图片描述
线程同步共有三种方法,每种方法又可以结合创建线程的两种方式来写,所以可以有6种写法,下面将通过卖票的例子来演示

Synchronized的两种方式

在这里插入图片描述
图解
在这里插入图片描述

方式一:同步代码块

在这里插入图片描述同步监视器可以理解为一把锁,所有对象都可以作为锁,某一条线程操作共享数据时会持有这把锁,其他线程也就不能操作共享数据,等操作共享数据结束后该线程会释放这把锁。需要注意的是,多个线程必须共用同一把锁!

下面我们通过模拟两个窗口同时卖出30张票来理解这个过程!

继承类使用同步代码块方式
package com.deserts.demo02;

public class Window01 {
   
    public static void main(String[] args) {
   
        Sell01 s1 = new Sell01();
        Sell01 s2 = new Sell01();
        s1.setName("窗口1");
        s2.setName("窗口2");
        s1.start();
        s2.start(
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值