多线程(一)

首先简单知道一下什么是进程(Process)、线程(Thread):

  • 进程:在操作系统中运行的就是进程,比如你的QQ,播放器,游戏等等。。。
  • 线程:一个进程可以有多个进程,如视频中同时可以听声音,看图像,看弹幕,等等

程序、进程、线程:

  • 说起进程,就不得不说下程序。程序是指令和数据的有序集合,其本身并没有任何运行的含义,是一个静态的概念。
  • 进程则是执行程序的一次执行过程,它是一个动态的概念。进程是系统资源分配的单位。
  • 通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在意义。线程是CPU调度和执行的单位。

注意:
很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,就像我们的大脑一样某一时刻只能做一件事,因为切换的很快,所以给我们同时执行的错觉。

核心概念:

  • 线程就是独立的执行路径
  • ◆在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程(垃圾回收之类的)
  • main()称之为主线程,为系统的入口,用于执行整个程序
  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器(CPU)安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的。
  • 同一份资源操作时,会存在资源抢夺的问题,(这里涉及到线程安全问题以及如何确保线程安全,以后专门再总结)需要加入并发控制;(比如10000个人抢100张票,不可能每个人都抢到,所以就需要加入控制,让他们排队。。。)
  • 线程会带来额外的开销,如cpu调度时间,并发控制开销(比如排队也会增加时间开销)。
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

创建线程

线程创建有三种主要方式:

  1. 继承Thread类(重点)(不推荐使用!!!)
  2. 实现Runnable接口(重点)(推荐使用!!!)
  3. 实现Callable接口(了解)

方式一:继承Thread类 (不推荐使用,避免OPP单继承的局限性):

继承Thread类创建线程步骤:

  1. 自定义线程类继承Thread类
  2. 重写run()方法,编写线程执行体(养成习惯,继承完Thread类后,紧接着重写run()方法 )
  3. 创建线程对象
  4. 调用start()方法

注意:线程不一定立即执行,而是由CPU安排调度!

示例一:继承Thread类创建多线程并启动

package com.kuang.thread;

/**
 * @ClassName demo01
 * @Description 线程创建 Thread Runnable Callable
 * @Author 麻虾
 * @Date 2021/4/22 23:35 35
 * @Version 1.0
 */

// 方式一:继承Thread类,重写run()方法,调用start开始线程

public class TestThread1 extends Thread {

    //养成习惯,继承完就重写run()方法
    //线程入口点
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("我在看代码----" + i);
        }
    }

    //主方法,即main线程,主线程
    public static void main(String[] args) {

        //开启另一个线程
        //创建一个线程对象
        TestThread1 testThread1 = new TestThread1();

        //调用start()方法启动线程
        testThread1.start();

        for (int i = 0; i < 20; i++) {
            System.out.println("我在学习多线程----" + i);
        }
    }
}

上述代码中,在主线程中调用start()方法启动线程,执行结果如下:

我们可以看出两条线程是不是先执行谁后执行谁的关系,而是由CPU安排调度交替执行的!

我在学习多线程----0
我在学习多线程----1
我在学习多线程----2
我在学习多线程----3
我在学习多线程----4
我在   看代码----0
我在看代码----1
我在看代码----2
我在看代码----3
我在学习多线程----5
我在学习多线程----6
我在学习多线程----7
我在看代码----4
我在看代码----5
我在看代码----6
我在看代码----7
我在学习多线程----8
我在学习多线程----9
我在学习多线程----10
我在学习多线程----11
我在学习多线程----12
我在学习多线程----13
我在学习多线程----14
我在学习多线程----15
我在学习多线程----16
我在学习多线程----17
我在学习多线程----18
我在学习多线程----19
我在看代码----8
我在看代码----9
我在看代码----10
我在看代码----11
我在看代码----12
我在看代码----13
我在看代码----14
我在看代码----15
我在看代码----16
我在看代码----17
我在看代码----18
我在看代码----19

下面我们更改主线程(主方法)里调用的方法,不调用start(),改为调用run()方法,看一下结果如何?

我在看代码----0
我在看代码----1
我在看代码----2
我在看代码----3
我在看代码----4
我在看代码----5
我在看代码----6
我在看代码----7
我在看代码----8
我在看代码----9
我在看代码----10
我在看代码----11
我在看代码----12
我在看代码----13
我在看代码----14
我在看代码----15
我在看代码----16
我在看代码----17
我在看代码----18
我在看代码----19
我在学习多线程----0
我在学习多线程----1
我在学习多线程----2
我在学习多线程----3
我在学习多线程----4
我在学习多线程----5
我在学习多线程----6
我在学习多线程----7
我在学习多线程----8
我在学习多线程----9
我在学习多线程----10
我在学习多线程----11
我在学习多线程----12
我在学习多线程----13
我在学习多线程----14
我在学习多线程----15
我在学习多线程----16
我在学习多线程----17
我在学习多线程----18
我在学习多线程----19

从以上结果可以看出,如果我们在主线程里调用run()方法,那么两个线程执行顺序就是:先执行子线程后主线程。

注意:
很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,就像我们的大脑一样某一时刻只能做一件事,因为切换的很快,所以给我们同时执行的错觉。

示例二:练习Thread:实现多线程同步下载图片

1.准备:

这个示例中我们会使用到commons-io这个包,可以通过commons-io工具包的基本使用这篇博客了解一下具体内容。本文不再作讲解。

首先到官网下载commons-io包(https://commons.apache.org/proper/commons-io/),我使用的是当前最新版commons-io-2.8.0。
下载解压后如图:在这里插入图片描述
复制commons-io-2.8.0.jar,在我们的项目里新建一个包:lib
在这里插入图片描述

将commons-io-2.8.0.jar复制到lib里面,然后右击lib,选择Add as Library…选项:
在这里插入图片描述
这样我们就可以使用了。

2.开始示例:
package com.kuang.thread;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

/**
 * @ClassName TestThread2
 * @Description 练习Thread,实现多线程头部下载图片  (用到commons-io 包)
 * @Author 麻虾
 * @Date 2021/4/23 8:42 42
 * @Version 1.0
 */
public class TestThread2 extends Thread{

    private String url;//网络图片地址
    private String name;//保存到文件名

    //有参构造器
    public TestThread2(String url,String name){
        this.url = url;
        this.name = name;
    }

    //下载图片线程的执行体run()
    @Override
    public void run() {
        //类实例化 创建一个对象
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url,name);//调用下载方法下载图片
        System.out.println("下载了文件,名为:" + name);
    }

    public static void main(String[] args) {
        //创建对象的本质其实就是调用构造器,我们定义了一个有参构造,
        // 所以此处构造器是调用的我们自定义的有参构造,
        // 而不是系统默认的无参构造,所以需要把参数写进去
        TestThread2 t1 = new TestThread2("https://www.kuangstudy.com/assert/course/c1/13.jpg","1.jpg");
        //再复制几个,Ctrl + D  快捷键  复制整行
        TestThread2 t2 = new TestThread2("https://www.kuangstudy.com/assert/course/c1/09.jpg","2.jpg");
        TestThread2 t3 = new TestThread2("https://www.kuangstudy.com/assert/course/c1/10.jpg","3.jpg");
        TestThread2 t4 = new TestThread2("https://www.kuangstudy.com/assert/course/c1/12.jpg","4.jpg");
        TestThread2 t5 = new TestThread2("https://www.kuangstudy.com/assert/course/c1/08.jpg","5.jpg");

        //启动线程
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();

    }

}

//下载器  下载工具类
class WebDownloader{
    //下载方法
    public void downloader(String url,String name){
        //url:远程路径   name:存储名
        //拷贝一个URL地址到一个文件
        try {
            //Commons IO是针对开发IO流功能的工具类库
            //FileUtils是其下的文件工具,复制url到文件
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,Downloader方法出现问题");
        }
    }
}

运行结果如下:

下载了文件,名为:4.jpg
下载了文件,名为:2.jpg
下载了文件,名为:3.jpg
下载了文件,名为:1.jpg
下载了文件,名为:5.jpg

由此结果我们可以看出,我们start了五个线程

//启动线程
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();

但是执行顺序并不是按照我们的启动顺序先后执的,而是交替执行的。

此时,这五张图片就已经被我们下载下来了:在这里插入图片描述

总结一下思路

  • 首先我们想下载图片,我们写了一个下载器,里面有一个下载方法
  • 通过FileUtils工具类,里面的copyURLToFile方法,根据图片URL下载图片,就是把一个图片变成一个文件
  • 然后实现一个线程类(继承Thread),然后用构造器丢入url和name
  • 紧接着重写run方法(线程的执行体),执行体里面创建一个下载器对象,调用下载方法,实现下图片
  • 最后,在主线程里(main方法),通过构造器创建了三个线程,然后start启动线程。
  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

无霸哥

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

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

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

打赏作者

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

抵扣说明:

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

余额充值