java之多线程的三种不同创建方式and通过多线程模拟龟兔赛跑

news2025/1/12 6:20:33

Process与Thread:

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

线程是CPU调度和执行的的单位

注意:
很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器,如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错局.

线程就是独立的执行路径:

在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;main()称之为主线程,为系统的入口,用于执行整个程序;

在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能认为的干预的。对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;线程会带来额外的开销,如cpu调度时间,并发控制开销。

每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

Thread:

线程是程序中执行的线程。 Java虚拟机允许应用程序同时执行多个执行线程。

注:线程不一定立即执行,CPU安排调度

创建线程的方式1:继承Thread类

举例:

public class Test_Thread extends Thread{//1:自定义线程类继承Thread类
    @Override
    public void run() {		//2:重写run()方法体部分
            for(int i=0;i<20;i++){
                System.out.println("我在看代码"+i);
            }
        }
        public static void main(String[]args){
        Test_Thread test_thread=new Test_Thread()://创建线程对象
        test_thread.start();//3:调用start()方法,开启线程
        for(int i=0;i<20;i++){
            System.out.println("我在学习多线程"+i);
        }
        }
    }

test_thread.start(); //调用start()方法,开启线程输出如下:

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

test_thread.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

以上两种方法的输出结果不同的原因如下:

普通方法调用和多线程:

在这里插入图片描述

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

实现同步下裁图片:

首先需要在官网下载commons-io-2.6.jar:传送门

右击:

举例:

import org.apache.commons.io.FileUtils;

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

//准备下载器
class webdownload {
    //下载方法
    public void downloader(String url, String name) {
        try {
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downloader方法出现问题");
        }
    }
}


public class Thread_test extends Thread{
    private String url;
    private String name;
    public Thread_test(String url,String name){
        this.url=url;
        this.name=name;
    }

    @Override
    public void run() {
        webdownload webdownload=new webdownload();
        webdownload.downloader(url,name);
        System.out.println("下载成功"+name);
    }

    public static void main(String[] args) {//进行下载
        Thread_test thread_test1=new Thread_test("https://img2.baidu.com/it/u=1395980100,2999837177&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1671987600&t=dc660191a7f7ef18d080deaffe75d548","name1");
        Thread_test thread_test2=new Thread_test("https://img1.baidu.com/it/u=1605341541,1182642759&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1671987600&t=a82bfa5ed4d6a73504bb53932cfefe3f","name2");
        Thread_test thread_test3=new Thread_test("https://img2.baidu.com/it/u=1395980100,2999837177&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1671987600&t=dc660191a7f7ef18d080deaffe75d548","name3") ;
        thread_test1.start();
        thread_test2.start();
        thread_test3.start();

    }
}

是通过多线程同步下载这三张图片,因此,每次下载的成功的先后顺序是不相同的,具体由CPU决定:

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

创建线程的方式2:实现Runnable:

注:推荐使用Runnable对象,因为Java单继承的局限性

举例:

public class Runnable_test implements Runnable {//实现Runnable接口
    @Override
    public void run() {//实现run()方法,编写线程执行体
        for(int i=0;i<20;i++){
            System.out.println("我在看代码"+i);
        }
    }



    public static void main(String[] args) {
        Runnable_test runnable_test=new Runnable_test();
        new Thread(runnable_test).start();//创建线程对象,调用start()方法启动线程
        for(int i=0;i<20;i++)
        {
            System.out.println("我在练习多线程"+i);
        }
    }
}

输出:

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

实现同步下载图片:

与上文中的Thread相比,只需要修改下述这两个地方:

从继承Thread变为实现Runnable接口:

在这里插入图片描述加粗样式需要单独创建线程对象,去调用start()方法:

在这里插入图片描述

继承Thread类与实现Runnable接口对比:

子类继承Thread类具备多线程能力
启动线程:子类对象start()
不建议使用: 避免OOP单继承局限性
实现接口Runnable具有多线程能力
启动线程:传入目标对象+Thread对象start()
推荐使用: 避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

多个线程操作同一个资源的情况:

线程不安全,数据紊乱

public class Runnable_test implements Runnable {//实现Runnable接口
private  int ticknumbers=10;

    @Override
    public void run() {
        while(true){
            if(ticknumbers<=0){
                break;
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticknumbers--+"票");//currentThread()监测线程的状态
        }
    }

    public static void main(String[] args) {
        Runnable_test runnable_test=new Runnable_test();
        new Thread(runnable_test,"小明").start();
        new Thread(runnable_test,"小黄").start();
        new Thread(runnable_test,"小红").start();
    }
}

在输出的数据中,显然出现了,一张票同时被大于1人拿到的情况,这与我们的现实显然不相符合

在这里插入图片描述

模拟"龟兔赛跑":

public class Race_test implements Runnable {
    public static void main(String[] args) {
        Race_test race_test=new Race_test();
        //创建线程对象
        new Thread(race_test,"乌龟").start();
        new Thread(race_test,"兔子").start();
    }
    private  static String winner;

    @Override
    public void run() {
        for(int i=0;i<=50;i++){//让兔子"每隔一段时间休息一下"
            if(Thread.currentThread().getName().equals("兔子")&&i%5==0) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            boolean flag=gameover(i);
            if(flag){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"--------->跑了"+i+"步");
        }
    }
    private  boolean gameover(int steps){//比赛规则
        if(winner!=null)
        {
            return true;
        }
        else{
            if(steps>=50) {//判出胜负,比赛结束
                winner=Thread.currentThread().getName();
                System.out.println("winner is"+winner);
                return true;
            }
        }
        return false;
    }
}

输出:

乌龟---->跑了0步
兔子---->跑了0步
乌龟---->跑了1步
兔子---->跑了1步
乌龟---->跑了2步
兔子---->跑了2步
乌龟---->跑了3步
兔子---->跑了3步
乌龟---->跑了4步
兔子---->跑了4步
乌龟---->跑了5步
乌龟---->跑了6步
乌龟---->跑了7步
乌龟---->跑了8步
乌龟---->跑了9步
乌龟---->跑了10步
乌龟---->跑了11步
乌龟---->跑了12步
乌龟---->跑了13步
乌龟---->跑了14步
乌龟---->跑了15步
乌龟---->跑了16步
乌龟---->跑了17步
乌龟---->跑了18步
乌龟---->跑了19步
乌龟---->跑了20步
乌龟---->跑了21步
乌龟---->跑了22步
乌龟---->跑了23步
乌龟---->跑了24步
乌龟---->跑了25步
乌龟---->跑了26步
乌龟---->跑了27步
乌龟---->跑了28步
乌龟---->跑了29步
乌龟---->跑了30步
乌龟---->跑了31步
乌龟---->跑了32步
乌龟---->跑了33步
乌龟---->跑了34步
乌龟---->跑了35步
乌龟---->跑了36步
乌龟---->跑了37步
乌龟---->跑了38步
乌龟---->跑了39步
乌龟---->跑了40步
乌龟---->跑了41步
乌龟---->跑了42步
乌龟---->跑了43步
乌龟---->跑了44步
乌龟---->跑了45步
乌龟---->跑了46步
乌龟---->跑了47步
兔子---->跑了5步
乌龟---->跑了48步
兔子---->跑了6步
乌龟---->跑了49步
兔子---->跑了7Winner is乌龟

创建线程的方式3:实现Callable接口 :

1.实现Callable接口,需要返回值类型----Boolean[注意是大写开头]
2.重写call方法,需要抛出异常
3.创建目标对象

//以下三步为Callable接口的固定步骤
4.创建执行服务: ExecutorService ser = Executors.newFixedThreadPool(1):
5.提交执行: Future<Boolean> result1 = ser.submit(t1);
6.获取结果: boolean r1 = result1.get()
7.关闭服务: ser.shutdownNow();

举例:

import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Callable_test implements Callable<Boolean> {
    public static void main(String[] args) {
        Callable_test callable_test1 = new Callable_test("https://img2.baidu.com/it/u=1395980100,2999837177&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1671987600&t=dc660191a7f7ef18d080deaffe75d548", "梅花1");
        Callable_test callable_test3 = new Callable_test("https://img2.baidu.com/it/u=1395980100,2999837177&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1671987600&t=dc660191a7f7ef18d080deaffe75d548", "梅花3");
        Callable_test callable_test2 = new Callable_test("https://img2.baidu.com/it/u=1395980100,2999837177&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1671987600&t=dc660191a7f7ef18d080deaffe75d548", "梅花2");
        //创建执行服务
        ExecutorService ser = Executors.newFixedThreadPool(3);
        //提交执行
        Future<Boolean> r1=ser.submit(callable_test1);
        Future<Boolean> r2= ser.submit(callable_test2);
        Future<Boolean>r3= ser.submit(callable_test3);
        //获取结果
        boolean rs = r1.get();
        boolean rs2 = r2.get();
        boolean rs3 = r3.get();
        //关闭服务
        ser.shutdownNow();
    }

    private String url;
    private String name;

    public Callable_test(String url,String name) {
        this.url=url;
        this.name=name;
    }

    @Override
    public Boolean call(){
            webdownload webdownload=new webdownload();
            webdownload.downloader(url,name);
            System.out.println("文件下载成功!");
        return true;
    }
}

//下载器
class webdownload {
    //下载方法
    public void downloader(String url, String name) {
        try {
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downloader方法出现问题");
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/112470.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

视频素材网,视频剪辑必备。

视频剪辑没素材&#xff0c;推荐6个网站帮你解决&#xff0c;免费可商用&#xff0c;建议收藏&#xff01; 1、菜鸟图库 视频素材下载_mp4视频大全 - 菜鸟图库 网站有超多视频素材&#xff0c;全部都是高清无水印&#xff0c;各种类型都有&#xff0c;像自然、城市、动物、科技…

自动控制原理笔记-线性系统的时域分析与校正

目录 时域法的概述&#xff1a; 时域法的作用和特点&#xff1a; 时域法常用的四个时间信号&#xff1a; 线性系统时域性能指标&#xff1a; 五个常用的性能指标&#xff1a; 一阶系统的时间响应及动态性能&#xff1a; 一阶系统动态指标的计算&#xff1a; 一阶系统的典型…

Github一夜爆火的阿里高并发技术小册究竟有什么魅力

阿里在农历2021到来之前却是又搞了一个大动作&#xff01;把阿里这一年在应对高并发流量的技术经验整合成一份技术小册开源分享供大家学习借鉴。我也是昨天才发现这份小册开源至Github上居然一夜爆火&#xff01; 看了小册之后才知道&#xff0c;原来阿里在应对高并发大流量时也…

python中的json数据和pyecharts模块入门

目录 一json数据格式 1.什么是json 2.json有什么用 3.json格式数据转化 4.python数据和json数据的相互转化 5.json总结 二.pyecharts模块入门 1.基础折线图 全局配置选项——set_global_opts方法 一json数据格式 1.什么是json JSON是一种轻量级的数据交互格式。可以按…

RabbitMQ 第一天 基础 3 RabbitMQ 快速入门 3.2 入门程序【消费者】

RabbitMQ 【黑马程序员RabbitMQ全套教程&#xff0c;rabbitmq消息中间件到实战】 文章目录RabbitMQ第一天 基础3 RabbitMQ 快速入门3.2 入门程序3.2.1 消费者3.2.2 小结第一天 基础 3 RabbitMQ 快速入门 3.2 入门程序 3.2.1 消费者 之前我们 已经完成了生产者的基本代码编…

客快物流大数据项目(九十八):ClickHouse的SQL函数

文章目录 ClickHouse的SQL函数 一、​​​​​​​​​​​​​​类型检测函数

Verilog刷题HDLBits——Exams/review2015 fancytimer

Verilog刷题HDLBits——Exams/review2015 fancytimer题目描述代码结果题目描述 This is the fifth component in a series of five exercises that builds a complex counter out of several smaller circuits. You may wish to do the four previous exercises first (counte…

gateway中的限流与熔断

目录 1. 限流的使用场景 2. gateway限流实现 2.1 前提&#xff1a; 2.2 导入依赖包 2.3 在项目配置文件中配置redis 2.4 开发限流需要的Bean 2.5 为服务配置限流参数 2.6 压力测试 3. 熔断 3.1 熔断的使用场景 3.2 熔断配置 1. 限流的使用场景 为什么限流 限流就是限…

【点云检测】OpenPCDet 教程系列 [1] 安装 与 ROS运行

前言 主要是介绍库的使用&#xff0c;做笔记区 首先搜索的时候有个问题 一直在我脑子里 hhh 就是MMlab其实还有一个叫mmdetection3d 的库&#xff0c;然后搜的时候发现 hhh 有网友和我一样的疑惑&#xff1a; OpenPCDet和mmdetection3d有什么区别 ? - 知乎 (zhihu.com) 这…

在无序数组中求第K小的数

在无序数组中求第 KKK 小的数 改写快排的方法 【思路】在该无序数组中 随机 选择一个数 vvv&#xff0c;拿 vvv 去做整个数组的荷兰国旗问题&#xff0c;即将数组分成三个区域 “小于vvv | 等于 vvv | 大于 vvv”&#xff0c;每个区域都不要求有序&#xff0c;不过等于 vvv 的…

学习记录-mybatis+vue+elementUi实现分页查询(后端部分)

这一部分的实现确实让我学到不少东西。不管是后端还是前端部分的实现。 首先需要明确的是&#xff0c;实现分页查询&#xff0c;我们需要从前端获取到几个参数&#xff1f;第一个是当前在第几页&#xff0c;第二个是每一页有多少个值。分别叫做&#xff1a;currentPage和pageSi…

Redis集群之AKF架构原理

当我们搭建集群之前&#xff0c;先要想明白需要解决哪些问题&#xff0c;搞清楚这个之前先回想一下单节点、单实例、单机有哪些问题&#xff1f; 单点故障&#xff1a;只有一台Redis的话&#xff0c;如果出现故障&#xff0c;那么整个服务都不可用缓存容量&#xff1a;单台Red…

【Django项目开发】用户注册模型类、序列化器类、视图类设计(三)

文章目录一、模型类设计1、Django认证系统提供了用户模型类User&#xff0c;为什么还要定义User模型类?2、AbstractUser3、自定义用户模型类的字段有4、User模型类编写好了就可以了吗?二、序列化器类设计1、注意2、单字段进行校验3、用户认证的时候为什么不用create,而用crea…

C++构造函数和析构函数

&#xff08;一&#xff09;构造函数 要点 定义&#xff1a;构造函数 &#xff0c;是一种特殊的方法。主要用来在创建对象时初始化对象&#xff0c; 即为对象成员变量赋初始值&#xff0c;总与new运算符一起使用在创建对象的语句中。特别的一个类可以有多个构造函数 &#xff0…

网络协议知识串讲-第38讲-用双十一的故事串起碎片的网络协议(中)

上一节我们讲到,手机App经过了一个复杂的过程,终于拿到了电商网站的SLB的IP地址,是不是该下单了? 别忙,俗话说的好,买东西要货比三家。大部分客户在购物之前要看很多商品图片,比来比去,最后好不容易才下决心,点了下单按钮。下单按钮一按,就要开始建立连接。建立连接…

Spring Cache(边路缓存)

一、Spring Cache介绍 Spring Cache 是Spring - context-xxx.jar中提供的功能&#xff0c;可以结合EHCache&#xff0c;Redis等缓存工具使用。给用户提供非常方便的缓存处理&#xff0c;缓存基本判断等操作,可以直接使用注解实现。 ​ 在包含了Spring - context-xxx.jar的Spri…

07---vue前端实现增删改查

前端VUE通过请求后端实现增删改查&#xff0c;文末会有前端完整代码 1、实现查询功能 一、实现三个条件一起查询 后台需要实现这三个条件的模糊查询 UserController.java //分页查询GetMapping("/page")public IPage<User> findPage(RequestParam Integer p…

【Jenkins】学习笔记

学习笔记一、Jenkins1.1、Jenkins的作用二、下载安装2.1、安装环境2.2、安装GitLab2.3、安装Jenkins三、Jenkins Git Maven 部署配置3.1、安装maven插件3.2、新建项目3.3、自动发布到测试服务器四、publish over ssh 配置4.1、超时机制4.2、shell的日志输出4.3、运行前清理五…

网络地址转换NAT

目录 IP 地址空间即将面临耗尽的危险 NAT 缓解 IP 地址空间耗尽的问题 NAT 的基本方法 VPN 的要点 IP 地址空间即将面临耗尽的危险 互联网采用了无分类编址方式、动态分配IP地址等措施来减缓IP地址空间耗尽的速度 但由于互联网用户数目的激增&#xff0c;特别是大量小型办公…

Linux搭建DHCP服务

DHCP(Dynamic Host Confifuration Protocol,动态主机配置协议)它可以为客户自动分配IP地址、以及缺省网关、DNS服务器的IP地址等TCP/IP参数。 简单说,就是在DHCP服务器上有一个存放着IP地址、网关、DNS等参数。当客户端请求使用时,服务器则负责将相应的参数分配给客户端,…