首页 > 其他分享 >线程介绍和使用

线程介绍和使用

时间:2023-05-19 11:32:44浏览次数:33  
标签:Runnable run Thread 介绍 start 线程 使用 public

1. 线程相关概念  579

1.1 程序(program)  579

是为完成特定任务、用某种语言编写的一组指令的集合。简单的说:就是我们写的代码

线程介绍和使用_多线程

1.2 进程  579

1.进程是指运行中的程序,比如我们使用QQ,就启动了一一个进程,操作系统就会为该进程分配内存空间。当我们使用迅雷,又启动了一个进程,操作系统将为迅雷分配新的内存空间。

2.进程是程序的一次执行过程,或是正在运行的一个程序。 是动态过程:有它自身的产生、存在和消亡的过程

线程介绍和使用_多线程_02

2. 什么是线程  579

1.线程由进程创建的,是进程的一个实体(例如你在迅雷下载一个东西,而这个东西就是一个线程)

2.一个进程可以拥有多个线程,如下图

线程介绍和使用_System_03

坦克大战[后面会把多线程加入到坦克大战中,学以致用]

线程介绍和使用_System_04

2.1 其他相关概念  580

1.单线程:同个时刻,只允许执行一个线程

2.多线程:同一个时刻,可以执行多个线程,比如:一个qq进程,可以同时打开多个聊天窗口,一个迅雷进程,可以同时下载多个文件

线程介绍和使用_ide_05

3.并发:同一个时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单的说,单核cpu实现的多任务就是并发。

线程介绍和使用_多线程_06

4.并行:同一个时刻,多个任务同时执行。多核cpu可以实现并行。

线程介绍和使用_多线程_07

2.2 例 查看当前电脑有多少cpu  580

代码在com.stulzl.cpunum_.

CupNum
package com.stulzl.cpunum_;

//查看当前电脑有多少cpu  580
public class CupNum {
    public static void main(String[] args) {
        //都是一些方法不必纠结
        Runtime runtime = Runtime.getRuntime();
        //获取当前电脑cpu数量
        int cpuNums = runtime.availableProcessors();
        System.out.println("当前cpu数量="+cpuNums);
    }
}

3. 线程基本使用  581

3.1 创建线程的两种方式  581

在java中线程来使用有两种方法。

1.继承Thread类 ,重写 run方法

2.实现Runnable接口, 重写run方法

线程介绍和使用_ide_08

3.2 线程应用案例 1-继承 Thread 类  581

1)请编写程序,开启一个线程, 该线程每隔1秒。在控制台输出"喵喵, 我是小猫咪”

2)对上题改进:当输出80次喵喵,我是小猫咪,结束该线程

3)使用JConsole监控线程执行情况,并画出程序示意图

线程介绍和使用_多线程_09

代码在com.stulzl.thread_use01.包中

ThreadUse01

package com.stulzl.thread_use01;

import java.util.TreeMap;

//线程应用案例 1-继承 Thread 类  581
//1)请编写程序,开启一个线程, 该线程每隔1秒。在控制台输出"喵喵, 我是小猫咪”
public class ThreadUse01 {
    public static void main(String[] args) throws InterruptedException {
        //创建Cat对象,可以当作线程使用
        Cat cat = new Cat();
        cat.start();//启动线程->最终会执行cat的run方法
        //读源码
        /*
        (1)
        public synchronized void start() {
            start0();
        }
        (2)
        //start0() 是本地方法,是 JVM 调用, 底层是 c/c++实现
        //真正实现多线程的效果, 是 start0(), 而不是 run
        private native void start0();
        */

        //之所以不直接调用 是因为run 方法就是一个普通的方法, 没有真正的启动一个线程
        // ,就会把 run 方法执行完毕,才向下执行
        //cat.run();

        //说明: 当 main 线程启动一个子线程 Thread-0, 主线程不会阻塞, 会继续执行
        System.out.println("主线程继续执行"+Thread.currentThread().getName());//名字main
        for (int i = 0; i <60; i++) {
            System.out.println("主线程 i="+i);
            //让主线程休息1秒
            Thread.sleep(1000);//这里偷个懒,就不捕获异常了,我们直接抛出异常

        }
    }
}
//说明
//1. 当一个类继承了 Thread 类, 该类就可以当做线程使用
//2. 我们会重写 run 方法,写上自己的业务代码
//3. run Thread 类 实现了 Runnable 接口的 run 方法
/*
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
*/
class Cat extends Thread{//继承Thread类,说明Cat就可以当作一个线程使用
    int count = 0;
    //重写润方法,写上自己的业务逻辑
    @Override
    public void run() {
        while(true){
            //该线程每隔1秒。在控制台输出"喵喵, 我是小猫咪”
            System.out.println("喵喵, 我是小猫咪"+(++count)+"线程名="+Thread.currentThread().getName());
            //让该线程休眠1秒
            try {
                Thread.sleep(1000);//1000毫秒时1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(count==80){//如果输出80次就退出循环,也就等于退出了线程
                break;
            }
        }
    }
}

3.3 解析为什么不直接调用run()方法而是用start()  583

之所以不直接调用 是因为run 方法就是一个普通的方法, 没有真正的启动一个线程 ,就会把 run 方法执行完毕,才向下执行,这样就会产生阻塞

start0() 是本地方法,是 JVM 调用, 底层是 c/c++实现,真正实现多线程的效果, 是 start0(), 而不是 run

在底层

start()方法调用start0()方法后(start0()方法真实是由JVM调用的,start0()方法才是真正调用run()方法的),该线程并不一定会立马执行,只是将线程变成了可运行状态。具

体什么时候执行,取决于CPU,由CPU统一调度。

线程介绍和使用_多线程_10

4. 线程应用案例 2-实现 Runnable 接口  584

➢说明

1. java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时在用继承Thread类方法来创建线程 显然不可能了。

2. java设计者们提供了另外一个方式创建线程,就是通过实现Runnable接口来创建线程

4.1 应用案例  584

请编写程序,该程序可以每隔1秒。在控制台输出"hi!" ,当输出10次后,自动退出。请使用实现Runnable接口的方式实现。

这里底层使用了设计模式[代理模式] =>代码模拟实现Runnable接口开发线程的机制

代码在com.stulzl.runnable_use.包中

Thread02
package com.stulzl.runnable_use;

// 线程应用案例 2-实现 Runnable 接口  584
//请编写程序,该程序可以每隔1秒。在控制台输出"hi!" ,当输出10次后,自动退出。
// 请使用实现Runnable接口的方式实现。
public class Thread02 {
    public static void main(String[] args) {
        Dog dog = new Dog();
        //dog.start(); 这里不能调用 start
        
        //创建了 Thread 对象,把 dog 对象(实现 Runnable),放入 Thread
        // 这里底层使用了设计模式[代理模式] =>代码模拟实现Runnable接口开发线程的机制
        
        //大白话解释,因为Dog是实现了Runnable接口 可是我们的Runnable接口中没有start()方法,
        // 但是呢,我们模拟的Thead类有start()方法,而我还想用start实现线程,
        // 就必须通过Thead类区去调用它的start()方法,巧了Thead类里有 有参构造器,
        // 这就意味着我们可以把dog类扔进这个有参构造器,就相当于我们的Dog类进去了(为了后面动态绑定),
        // 然后在调用 Thead类的start()方法即可,后涉及动态绑定等操作就实现了线程
        
        //这个有参构造器接受的还是Runnable类型的,因为Dog实现了Runnable接口所以可以扔进有参构造器去
        Thread thread = new Thread(dog);
        thread.start();

        //这里是我们模拟实现的代理模式
        //大白话解释,因为tiger是实现了Runnable接口 可是我们的Runnable接口中没有start()方法,
        // 但是呢,我们模拟的theadProxy类有start()方法,我还想用start实现线程,
        // 就必须通过theadProxy类区去调用它的start()方法,巧了theadProxy类里有构造器 还是有参构造器,
        // 这就意味着我们可以把tiger类扔进这个有参构造器,就相当于我们的Tiger类进去了,然后在调用
        //theadProxy类的start()方法即可,后涉及动态绑定等操作就实现了线程
//        Tiger tiger = new Tiger();
//        TheadProxy theadProxy = new TheadProxy(tiger);//因为tiger也实现了Runnable接口所以可以扔进去
//        theadProxy.start();

    }
}

class Dog implements Runnable{//通过实现 Runnable 接口,开发线程
    int count = 0;
    @Override
    public void run() {
        while(true){
            System.out.println("小狗汪汪叫...hi"+(++count)+Thread.currentThread().getName());
            //休眠1秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(count==10){
                break;
            }
        }
    }
}


class Animal{}
class Tiger extends Animal implements Runnable{
    @Override
    public void run() {
        System.out.println("老虎嗷嗷叫……");
    }
}
//这里底层使用了设计模式[代理模式] =>代码模拟实现Runnable接口开发线程的机制
//线程代理
//线程代理类 , 模拟了一个极简的 Thread 类
class TheadProxy implements Runnable{//TheadProxy是我们模拟的,可以看作系统的Thead
    private Runnable target = null;//属性,类型是Runnable

    //构造器
    public TheadProxy(Runnable target){
        this.target = target;
    }
    public void start(){
        start0();
    }
    public void start0(){
        run();
    }

    @Override
    public void run() {
        if(target!=null){
            target.run();//动态绑定
        }
    }
}

5. 线程使用应用案例-多线程执行  585

请编写一个程序,创建两个线程,一个线程每隔1秒输出"hello,world" ,输出10次,退出,一个线程每隔1秒输出"hi" ,输出5次退出

代码在com.stulzl.thread03.包中

Thread03

package com.stulzl.thread03;

//线程使用应用案例-多线程执行  585
//请编写一个程序,创建两个线程,一个线程每隔1秒输出"hello,world" ,输出10次,退出,
// 一个线程每隔1秒输出"hi" ,输出5次退出
public class Thread03 {
    public static void main(String[] args) {
        T1 t1 = new T1();
        Thread thread1 = new Thread(t1);
        thread1.start();

        T2 t2 = new T2();
        Thread thread2 = new Thread(t2);
        thread2.start();
    }
}
class T1 implements Runnable{
    private int count = 0;
    @Override
    public void run() {
        while(true){
            //一个线程每隔1秒输出"hello,world" ,输出10次,退出
            System.out.println("hello,world"+(++count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(count==10){
                break;
            }
        }
    }
}
class T2 implements Runnable{
    private int count = 0;
    @Override
    public void run() {
        while(true){
            //一个线程每隔1秒输出"hi" ,输出5次退出
            System.out.println("hi"+(++count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(count==5){
                break;
            }
        }
    }
}

6. 线程如何理解  585

线程介绍和使用_多线程_11

线程介绍和使用_多线程_12

7. 继承 Thread vs 实现 Runnable 的区别

1.从java的设计来看,通过继承Thread或者实现Runnable接口来创建线程本质上没有区别,从jdk帮助文档我们可以看到Thread类本身就实现了Runnable接口

2.实现Runnable接口方式更加适合多个线程共享一 个资源的情况,并且避免了单继承的限制,建议使用Runnable

8. 多线程练习  586

 [售票系统] ,编程模拟三个售票窗口售票100,分别使用继承Thread和实现Runnable方式,并分析有什么问题? 

提示会出现超卖现象,等后面我们再把这个坑上

代码在com.stulzl.ticket.包中

SellTicket

package com.stulzl.ticket;

//多线程练习  586
// [售票系统] ,编程模拟三个售票窗口售票100张,分别使用继承Thread和实现Runnable方式,并分析有什么问题?
public class SellTicket {
    public static void main(String[] args) {
//        SellTicket01 sellTicket01 = new SellTicket01();
//        SellTicket01 sellTicket02 = new SellTicket01();
//        SellTicket01 sellTicket03 = new SellTicket01();
//        //这里会出现超卖现象,原因是比如 当我们线程1进去后还没来的及--ticketNum,线程2线程3也进来了
//        //这样就会出现超卖现象
//        sellTicket01.start();//启动售票,开启线程
//        sellTicket02.start();
//        sellTicket03.start();

        //这样也会出现超卖现象,原因如上
        System.out.println("===使用实现接口的方式===");
        SellTicket02 sellTicket02 = new SellTicket02();
        new Thread(sellTicket02).start();//第一个线程窗口
        new Thread(sellTicket02).start();//第二个线程窗口
        new Thread(sellTicket02).start();//第三个线程窗口
    }
}
//使用继承Thread类
class SellTicket01 extends Thread{
    //让多个线程共享ticketNum,因为静态的会随着类的加载而创建的嘛,而且只会被创建一次,因为我们后三个线程
    //都要多ticketNum进行--,但是呢又不能让ticketNum重置,所以用static
    private static int  ticketNum = 100;
    @Override
    public void run() {
        while(true){
            if(ticketNum<=0){
                break;
            }
            //休眠50毫秒
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 "+Thread.currentThread().getName()+"售出一张票"+
                    "剩余票数="+(--ticketNum));
        }
    }
}

//实现接口Runnable接口
class SellTicket02 implements Runnable{
    private int ticketNum = 100;//让多个线程共享ticketNum
    @Override
    public void run() {
        while(true){
            if(ticketNum<=0){
                break;
            }
            //休眠50毫秒
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 "+Thread.currentThread().getName()+"售出一张票"+
                    "剩余票数="+(--ticketNum));
        }
    }
}

标签:Runnable,run,Thread,介绍,start,线程,使用,public
From: https://blog.51cto.com/u_15784725/6309683

相关文章

  • 线程终止+常用方法
    1. 基本说明  5871.当线程完成任务后,会自动退出。2.还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式2. 应用案例  587需求:启动一个线程t,要求在main线程中去停止线程t,请编程实现.代码在com.stulzl.exit_.包中ThreadExit_ packagecom.stulzl.exit_;//线程......
  • N4、使用Word2vec实现文本分类
    ......
  • k8s 1.23.0 安装使用ingress 1.1.1
    1、部署ingresscontroller下载yaml文件,要指定版本wgethttps://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.1/deploy/static/provider/cloud/deploy.yaml2、修改配置文件中的镜像下载地址,总共3处修改image:registry.cn-hangzhou.aliy......
  • AZC智能集成式电力电容器的原理与优点介绍
    安科瑞虞佳豪随着国家对农村建设的大力支持,农村生活条件不断改善,用电量也不断加大,逐渐暴露出农村低电压的现象;以及大量单相家用电器设备使用,将导致三相负荷不平衡,配电网电压波动大,严重时则会损坏用电设备,因此,农村对电压质量的要求也越来越高。现农村配电网大部分仍使用传统无功补......
  • java正确开发系列:使用hutool计算出时间段范围内的每一天
    背景:前端入参分别有startDate和endDate,类型为字符串,格式为:2023-01-01、2023-05-01,需要后端计算出1月到5月的每一天 代码如下:StringstartDateStr=res.getStartDate();StringendDateStr=res.getEndDate();DateTimestartDate=DateUtil.pars......
  • java整型 浮点型简单使用案例
    publicclassImoocStudent{publicstaticvoidmain(String[]args){inti1=2,i2=4;inti3=i1/i2;System.out.println("整型2除以整型4的结果为:"+i3);floatf1=2f,f2=4f;floatf3=f1/f2;......
  • ET介绍—— 一切皆实体的设计
    一切皆实体目前十分流行ECS设计,主要是守望先锋的成功,引爆了这种技术。守望先锋采用了状态帧这种网络技术,客户端会进行预测,预测不准需要进行回滚,由于组件式的设计,回滚可以只回滚某些组件即可。ECS最重要的设计是逻辑跟数据的完全分离。即EC是纯数据,System实际上就是逻辑,由数据驱动......
  • 使用Ansible OpenStack SDK创建OpenStack云主机
    安装OpenStackSDKpipinstallopenstacksdk安装Ansiblepipinstallansible创建一个Ansibleplaybook文件,例如create_instance.yml,并添加以下内容:----hosts:localhostgather_facts:notasks:-name:Createaninstanceos_server:state:p......
  • 一个.Net开发的功能强大、易于使用的流媒体服务器和管理系统
    推荐一个视频管理系统,非常适合个人或者公司打造视频网站。项目简介这是基于.NetCore开发的,跨平台的开源项目;支持多种音视频格式,如MP3、MP4、AVI、WMV、FLV等;支持本地管理与远程管理,让管理员可以轻松的管理视频资源。而且该项目还提供多平台的客户端,支持Web、桌面、Liunx、安卓......
  • 使用Ansible OpenStack SDK获取OpenStack的云主机列表
    1、使用Ansible的os_server_info模块可以获取OpenStack中所有云主机的列表。在使用该模块之前,需要确保已经安装了OpenStackSDK。以下是使用os_server_info模块获取所有云主机列表的示例AnsiblePlaybook:-name:Getlistofallservershosts:localhostgather......