多线程一,什么是多线程,创建多线程的几种方式-程序员宅基地

技术标签: java  多线程  java后端开发  

1 多线程相关概念

并发(Concurrent):在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。
同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。

并行(Parallel):当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。其实决定并行的因素不是CPU的数量,而是CPU的核心数量,比如一个CPU多个核也可以并行。
进程(Process):是正在运行的程序实体,并且包括这个运行的程序中占据的所有系统资源,比如说CPU(寄存器),IO,内存,网络资源等。同样一个程序,同一时刻被两次运行了,那么他们就是两个独立的进程。
线程(Thread):是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

进程与线程的区别

  • 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;进程是系统资源分配的单位,线程是系统调度的单位。
  • 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线;
  • 进程之间相互独立,进程之间不能共享资源,而线程共享所在进程的地址空间和其它资源。同时线程还有自己的栈和栈指针,程序计数器等寄存器。
  • 调度和切换:线程上下文切换比进程上下文切换要快得多。

2 多线程概念

多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理“。

3 多线程优缺点

优点:
(1) 用户界面可以在进行其它工作的同时CPU一直处于活动状态,可以让程序运行速度更快。
(2)占用大量处理时间的任务可以定期将处理器时间让给其它任务,可以提高CPU利用率。
使用实例演示多线程好处(在了解了多线程实现方式之后进行编写)

package com.aaa.mt.demo3;
import java.io.*;
/**
 * @ fileName:PingIP
 * @ description:
 * @ author:zhz
 * @ createTime:2021/11/30 10:58
 * @ version:1.0.0
 */
public class PingIP {
    
    /**
     * 封装pingIP方法
     * @param ip
     */
    public static void pingIP(String ip){
    
        InputStream inputStream = null;
        BufferedReader bufferedReader = null;
        try {
    
            //Runtime每个Java应用程序都有一个Runtime类的Runtime ,允许应用程序与运行应用程序的环境进行接口。
            Runtime runtime = Runtime.getRuntime();
            //使用与环境接口的类的exec 可以得到当前运行进程类
            Process process = runtime.exec("ping  "+ip);
            //获取到执行结果流
            inputStream = process.getInputStream();
            //BufferedReader使用按行读取的字符流并提高效率
            //InputStreamReader   把字节流转字符流的类
            bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"GBK"));
            //定义变量接受读的字符串
            String charLine = null;
            //定义是否ping得通得标识符
            boolean isSuc =false;
            //按行读取
            while((charLine=bufferedReader.readLine())!=null){
    
                if(charLine.indexOf("TTL")!=-1){
    
                    isSuc=true;
                    break;
                }
            }
            if(isSuc){
    
                System.out.println(ip+"通");
            }else{
    
                System.out.println(ip+"不通");
            }
        } catch (IOException e) {
    
            e.printStackTrace();
        } finally {
    
            try {
    
                if(inputStream!=null){
    
                    inputStream.close();
                }
                if(bufferedReader!=null){
    
                    bufferedReader.close();
                }
            } catch (IOException e) {
    
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
    
        //返回1970-1-1午夜到现在的毫秒数
        long startTime = System.currentTimeMillis();
        for (int i = 1; i <= 20; i++) {
    
            pingIP("192.168.41."+i);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("用时:"+(endTime-startTime)/1000);
    }
}

package com.aaa.mt.demo3;
/**
 * @ fileName:MTPingIP
 * @ description:
 * @ author:zhz
 * @ createTime:2021/11/30 11:24
 * @ version:1.0.0
 */
public class MTPingIP extends Thread {
    
    private  String ip;
    public MTPingIP(String ip) {
    
        this.ip = ip;
    }
    @Override
    public void run() {
    
        //执行业务代码
          PingIP.pingIP(ip);
    }
}

package com.aaa.mt.demo3;
/**
 * @ fileName:Test
 * @ description:
 * @ author:zhz
 * @ createTime:2021/11/30 11:27
 * @ version:1.0.0
 */
public class Test {
    
    public static void main(String[] args) {
    
        //返回1970-1-1午夜到现在的毫秒数
        long startTime = System.currentTimeMillis();
        // main线程  它和启动20个线程是并行运行
        System.out.println("当前线程名称"+Thread.currentThread().getName());
        for (int i = 1; i <= 20; i++) {
    
            //循环启动20线程   20个线程一起ping
            new MTPingIP("192.168.41."+i).start();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("用时:"+(endTime-startTime)/1000);
    }
}

缺点
(1)等候使用共享资源时造成程序的运行速度变慢。这些共享资源主要是独占性的资源 ,如打印机等。
(2)对线程进行管理要求额外的 CPU开销,线程的使用会给系统带来上下文切换的额外负担。
(3)线程的死锁。即对共享资源加锁实现同步的过程中可能会死锁。(后面有实例)

4 多线程实现方式

1,继承thread

package com.aaa.mt.demo1;
/**
 * @ fileName:MTExtendsThread
 * @ description:继承Thread完成多线程
 * @ author:zhz
 * @ createTime:2021/11/30 9:22
 * @ version:1.0.0
 */
public class MTExtendsThread  extends Thread{
    
    @Override
    public void run() {
    
         //执行线程业务的方法
        for (int i = 0; i < 10; i++) {
    
            //返回正在执行的线程实例
            Thread thread = Thread.currentThread();
            //getName()获取当前线程实例的名称
            System.out.println(thread.getName()+"执行打印"+i);
        }
    }
}

2 , 实现Runnable

package com.aaa.mt.demo1;
/**
 * @ fileName:MTImplementsRunnable
 * @ description:
 * @ author:zhz
 * @ createTime:2021/11/30 9:36
 * @ version:1.0.0
 */
public class MTImplementsRunnable  implements Runnable{
    
    @Override
    public void run() {
    
        //执行线程业务的方法
        for (int i = 0; i < 10; i++) {
    
            //返回正在执行的线程实例
            Thread thread = Thread.currentThread();
            //getName()获取当前线程实例的名称
            System.out.println(thread.getName()+"执行打印"+i);
        }
    }
}

3,实现Callable
package com.aaa.mt.demo1;
import java.util.UUID;
import java.util.concurrent.Callable;
/**

  • @ fileName:MTImplementsCallable
  • @ description:
  • @ author:zhz
  • @ createTime:2021/11/30 9:44
  • @ version:1.0.0
    */
public class MTImplementsCallable implements Callable {
    
    @Override
    public Object call() throws Exception {
    
        //执行线程业务的方法
        for (int i = 0; i < 10; i++) {
    
            //返回正在执行的线程实例
            Thread thread = Thread.currentThread();
            //getName()获取当前线程实例的名称
            System.out.println(thread.getName()+"执行打印"+i);
        }
        //获取随机数
        UUID uuid = UUID.randomUUID();
        System.out.println("随机数字符串为:"+uuid);
        System.out.println(1/0);
        return uuid;
    }
}

4,使用线程池

package com.aaa.mt.demo1;

/**
 * @ fileName:MTPool
 * @ description:
 * @ author:zhz
 * @ createTime:2021/11/30 10:30
 * @ version:1.0.0
 */
public class MTPool implements Runnable {
    
    @Override
    public void run() {
    
        //执行线程业务的方法
        for (int i = 0; i < 10; i++) {
    
            //返回正在执行的线程实例
            Thread thread = Thread.currentThread();
            //getName()获取当前线程实例的名称
            System.out.println(thread.getName()+"执行打印"+i);
        }
    }

}

//实例化线程类
MTPool mtPool =new MTPool();
//使用Executors创建固定长度为4的线程池
ExecutorService executorService = Executors.newFixedThreadPool(4);
//调用execute启动线程
executorService.execute(mtPool);

5 实现runnable和callable的区别

1,都是执行多线程,但是方法名称不同 run() 和call()
2, 实现Runnable方法是没有返回值,无法获取线程业务方法执行结果 而Callable相反
3, 实现Runnable方法没有抛出异常 而Callable有异常处理,并且获取异常

6 继承thread和实现runnable接口的区别

一个类只能继承一个父类,存在局限;一个类可以实现多个接口。在实现Runnable接口的时候调用Thread的Thread(Runnable run)或者Thread(Runnable run,String name)构造方法创建进程时,使用同一个Runnable实例,建立的多线程的实例变量也是共享的;但是通过继承Thread类是不能用一个实例建立多个线程,故而实现Runnable接口适合于资源共享;当然,继承Thread类也能够共享变量,能共享Thread类的static变量;
使用卖票实例演示上面表述:

package com.aaa.mt.demo2;
/**
 * @ fileName:SellTicketExtendsThread
 * @ description: 使用多线程卖票,继承Thread来实现业务
 * @ author:zhz
 * @ createTime:2021/11/30 10:40
 * @ version:1.0.0
 */
public class SellTicketExtendsThread extends Thread {
    
    //定义总票数
    private int ticketNum = 20;
    //卖票人姓名
    private String  name;
    /**
     * 构造方法
     * @param name
     */
    public SellTicketExtendsThread(String name) {
    
        this.name = name;
    }
    @Override
    public void run() {
    
         while(ticketNum>0){
    
             System.out.println(name+"正在卖票,剩余"+(--ticketNum)+"张");
         }
    }

}

package com.aaa.mt.demo2;
/**
 * @ fileName:SellTicketImplementsRunnable
 * @ description:
 * @ author:zhz
 * @ createTime:2021/11/30 10:49
 * @ version:1.0.0
 */
public class SellTicketImplementsRunnable  implements Runnable{
    
    //定义总票数
    private int ticketNum = 20;
    @Override
    public void run() {
    
        while(ticketNum>0){
    
            System.out.println(Thread.currentThread().getName()+"正在卖票,剩余"+(--ticketNum)+"张");
        }
    }
}
package com.aaa.mt.demo2;
/**
 * @ fileName:Test
 * @ description:
 * @ author:zhz
 * @ createTime:2021/11/30 10:45
 * @ version:1.0.0
 */
public class Test {
    
    public static void main(String[] args) {
    
      /*  SellTicketExtendsThread sellTicketExtendsThread =new SellTicketExtendsThread("马云");
        sellTicketExtendsThread.start();
       *//* sellTicketExtendsThread.start();
        sellTicketExtendsThread.start();*//*
        SellTicketExtendsThread sellTicketExtendsThread1 =new SellTicketExtendsThread("马化腾");
        sellTicketExtendsThread1.start();
        SellTicketExtendsThread sellTicketExtendsThread2 =new SellTicketExtendsThread("刘强东");
        sellTicketExtendsThread2.start();*/
        SellTicketImplementsRunnable sellTicketImplementsRunnable =new SellTicketImplementsRunnable();
        new Thread(sellTicketImplementsRunnable,"马云").start();
        new Thread(sellTicketImplementsRunnable,"马化腾").start();
        new Thread(sellTicketImplementsRunnable,"刘强东").start();
    }
}

7 多线程匿名(Anonymous)内部类用法

package com.aaa.mt.demo4;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
/**
 * @ fileName:AnonymousInternalClass
 * @ description:
 * @ author:zhz
 * @ createTime:2021/11/30 11:35
 * @ version:1.0.0
 */
public class AnonymousInternalClass {
    
    public static void main(String[] args) {
    
        //继承Thread启动线程
        new Thread(){
    
            @Override
            public void run() {
    
                for (int i = 0; i < 10; i++) {
    
                    //返回正在执行的线程实例
                    Thread thread = Thread.currentThread();
                    //getName()获取当前线程实例的名称
                    System.out.println(thread.getName()+"执行打印"+i);
                }
            }
        }.start();
        System.out.println("-------------------------------------");
        //实现Runnable
        new Thread(new Runnable() {
    
            @Override
            public void run() {
    
                for (int i = 0; i < 10; i++) {
    
                    //返回正在执行的线程实例
                    Thread thread = Thread.currentThread();
                    //getName()获取当前线程实例的名称
                    System.out.println(thread.getName()+"执行打印"+i);
                }
            }
        }).start();
        System.out.println("-------------------------------------");
        //实现Callable
        new Thread(new FutureTask<String>(
                new Callable<String>() {
    
            @Override
            public String call() throws Exception {
    
                for (int i = 0; i < 10; i++) {
    
                    //返回正在执行的线程实例
                    Thread thread = Thread.currentThread();
                    //getName()获取当前线程实例的名称
                    System.out.println(thread.getName()+"执行打印"+i);
                }
                return null;
            }
        })).start();
    }
}

8 Thread常用方法

currentThread() 返回对当前正在执行的线程对象的引用。
setName(String name) 将此线程的名称更改为等于参数 name 。(具体用法参考项目demo4包)
getName() 返回此线程的名称。
start() 导致此线程开始执行; Java虚拟机调用此线程的run方法。
run() 如果这个线程使用单独的Runnable运行对象构造,则调用该Runnable对象的run方法; 否则,此方法不执行任何操作并返回。 线程执行业务的方法。
setPriority(int newPriority) 更改此线程的优先级。
getPriority() 返回此线程的优先级。
sleep(long millis) 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。
stop() 强制结束当前线程。Deprecated
join() 等待这个线程死亡。一旦调用一个线程的join方法后,当前线程产生阻塞,直到在调用 join方法的线程执行完毕后,再去执行。
yield() 对调度程序的一个暗示,即当前线程愿意暂时让出当前使用的处理器。当前让出执行权。

8 多线程状态

五种状态:

  • 新建(程序还没有开始运行线程中的代码)
  • 就绪(当start()方法返回后,线程就处于就绪状态,处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间)
  • 运行(线程获得CPU时间后,它才进入运行状态,真正开始执行run())
  • 阻塞(等待wait、带超时的等待sleep)
  • 终止(死亡,正常退出或者异常终止)
    在这里插入图片描述
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wang121213145/article/details/123828346

智能推荐

JWT(Json Web Token)实现无状态登录_无状态token登录-程序员宅基地

文章浏览阅读685次。1.1.什么是有状态?有状态服务,即服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如tomcat中的session。例如登录:用户登录后,我们把登录者的信息保存在服务端session中,并且给用户一个cookie值,记录对应的session。然后下次请求,用户携带cookie值来,我们就能识别到对应session,从而找到用户的信息。缺点是什么?服务端保存大量数据,增加服务端压力 服务端保存用户状态,无法进行水平扩展 客户端请求依赖服务.._无状态token登录

SDUT OJ逆置正整数-程序员宅基地

文章浏览阅读293次。SDUT OnlineJudge#include<iostream>using namespace std;int main(){int a,b,c,d;cin>>a;b=a%10;c=a/10%10;d=a/100%10;int key[3];key[0]=b;key[1]=c;key[2]=d;for(int i = 0;i<3;i++){ if(key[i]!=0) { cout<<key[i.

年终奖盲区_年终奖盲区表-程序员宅基地

文章浏览阅读2.2k次。年终奖采用的平均每月的收入来评定缴税级数的,速算扣除数也按照月份计算出来,但是最终减去的也是一个月的速算扣除数。为什么这么做呢,这样的收的税更多啊,年终也是一个月的收入,凭什么减去12*速算扣除数了?这个霸道(不要脸)的说法,我们只能合理避免的这些跨级的区域了,那具体是那些区域呢?可以参考下面的表格:年终奖一列标红的一对便是盲区的上下线,发放年终奖的数额一定一定要避免这个区域,不然公司多花了钱..._年终奖盲区表

matlab 提取struct结构体中某个字段所有变量的值_matlab读取struct类型数据中的值-程序员宅基地

文章浏览阅读7.5k次,点赞5次,收藏19次。matlab结构体struct字段变量值提取_matlab读取struct类型数据中的值

Android fragment的用法_android reader fragment-程序员宅基地

文章浏览阅读4.8k次。1,什么情况下使用fragment通常用来作为一个activity的用户界面的一部分例如, 一个新闻应用可以在屏幕左侧使用一个fragment来展示一个文章的列表,然后在屏幕右侧使用另一个fragment来展示一篇文章 – 2个fragment并排显示在相同的一个activity中,并且每一个fragment拥有它自己的一套生命周期回调方法,并且处理它们自己的用户输_android reader fragment

FFT of waveIn audio signals-程序员宅基地

文章浏览阅读2.8k次。FFT of waveIn audio signalsBy Aqiruse An article on using the Fast Fourier Transform on audio signals. IntroductionThe Fast Fourier Transform (FFT) allows users to view the spectrum content of _fft of wavein audio signals

随便推点

Awesome Mac:收集的非常全面好用的Mac应用程序、软件以及工具_awesomemac-程序员宅基地

文章浏览阅读5.9k次。https://jaywcjlove.github.io/awesome-mac/ 这个仓库主要是收集非常好用的Mac应用程序、软件以及工具,主要面向开发者和设计师。有这个想法是因为我最近发了一篇较为火爆的涨粉儿微信公众号文章《工具武装的前端开发工程师》,于是建了这么一个仓库,持续更新作为补充,搜集更多好用的软件工具。请Star、Pull Request或者使劲搓它 issu_awesomemac

java前端技术---jquery基础详解_简介java中jquery技术-程序员宅基地

文章浏览阅读616次。一.jquery简介 jQuery是一个快速的,简洁的javaScript库,使用户能更方便地处理HTML documents、events、实现动画效果,并且方便地为网站提供AJAX交互 jQuery 的功能概括1、html 的元素选取2、html的元素操作3、html dom遍历和修改4、js特效和动画效果5、css操作6、html事件操作7、ajax_简介java中jquery技术

Ant Design Table换滚动条的样式_ant design ::-webkit-scrollbar-corner-程序员宅基地

文章浏览阅读1.6w次,点赞5次,收藏19次。我修改的是表格的固定列滚动而产生的滚动条引用Table的组件的css文件中加入下面的样式:.ant-table-body{ &amp;amp;::-webkit-scrollbar { height: 5px; } &amp;amp;::-webkit-scrollbar-thumb { border-radius: 5px; -webkit-box..._ant design ::-webkit-scrollbar-corner

javaWeb毕设分享 健身俱乐部会员管理系统【源码+论文】-程序员宅基地

文章浏览阅读269次。基于JSP的健身俱乐部会员管理系统项目分享:见文末!

论文开题报告怎么写?_开题报告研究难点-程序员宅基地

文章浏览阅读1.8k次,点赞2次,收藏15次。同学们,是不是又到了一年一度写开题报告的时候呀?是不是还在为不知道论文的开题报告怎么写而苦恼?Take it easy!我带着倾尽我所有开题报告写作经验总结出来的最强保姆级开题报告解说来啦,一定让你脱胎换骨,顺利拿下开题报告这个高塔,你确定还不赶快点赞收藏学起来吗?_开题报告研究难点

原生JS 与 VUE获取父级、子级、兄弟节点的方法 及一些DOM对象的获取_获取子节点的路径 vue-程序员宅基地

文章浏览阅读6k次,点赞4次,收藏17次。原生先获取对象var a = document.getElementById("dom");vue先添加ref <div class="" ref="divBox">获取对象let a = this.$refs.divBox获取父、子、兄弟节点方法var b = a.childNodes; 获取a的全部子节点 var c = a.parentNode; 获取a的父节点var d = a.nextSbiling; 获取a的下一个兄弟节点 var e = a.previ_获取子节点的路径 vue