按照顺序执行_问一个多线程的问题:如何才能保证线程有序执行?

8bebc31b8c89acaf4dc8fe7bfa45d625.png

面试的时候你是否经常被问到这样的问题:

你一般通过什么方式去控制线程的执行顺序?

碰到这样的问题,我的内心其实是很抵触的!

de36c9821d3cd83aa1e0e8c4b99abf36.png

开什么玩笑?我怎么会控制它呢?我为什么要控制它?

其实不用慌,这个问题并不难,且听我慢慢道来......

a7c169555878d87677e082203cc16c75.png

一、那么,什么是线程、进程?

要想控制多线程的顺序,你首先应该搞清楚线程和进程到底是什么东西

1、进程

进程其实是操作系统的基础,是系统中一次程序的执行,也是一次程序和数据在机器上顺序执行时所发生的活动,又是系统进行资源分配和调度的一个独立单位。

其实说的通俗一点,可以这么理解,进程就是Windows系统中执行的一个exe程序,是操作系统管理的基本运行单元,看下面这个图你就知道啥是进程了!

74d57595b3b220ba630418714a235103.png

2、线程

线程是比进程还要小的一个单元,它是进程中独立运行的子任务。

你比如说一个微信.exe程序进程中就有非常多的子线程在同时运行,例如音视频线程、文件下载线程、信息传输线程等等,这些不同的任务如果都在一个线程去运行,那程序必定会特别慢,大家的体验不会像现在那样舒服,所以这里的每一个任务或功能都需要对应一个后台的线程在默默运行。

简单来说,线程就是组成进程的一条路径,一个进程可以包含一个或者多个线程。

二、什么是多线程环境?

关于多线程的环境,其实大家在使用Windows系统的时候就深有感触。

想象你一边在用IDE码代码,一边要和朋友聊天,还一边带着耳机听着音乐,你的系统为什么能够同时提供给你那么多服务呢?

其实这就是一个典型的多线程环境,你的电脑的CPU正在不断的从这些任务中飞速切换来处理程序中的各种事情,由于计算机的切换处理速度非常的快,所以你没办法在界面上进行感知,给我们的感觉就是他们其实在同时为我们服务着,这也是多线程环境的一种优势!

用一张图的方式来对比感受一下多线程的环境,例如:

61ff0ec7b756b647866afd4aade4cabf.png

在上图中,单线程环境下一号、二号处理任务是完全独立的两个任务,但是二号任务必须要等待1号任务处理完成才能执行,也就是二号处理任务必须要在程序开始后的5秒后才能运行到,最终的运行时间为15秒。

但是在多线程环境下,一号、二号虽然也是完全独立的任务,处在同一个进程中,但却由不同的线程去处理,CPU可以在这两个不同的线程之间进行切换,所以二号处理任务不需要在一号处理完成之后再处理,而是做异步处理,最终的运行时间也差不多在10秒左右。

三、几个关于CPU、线程执行的知识点

1、在单CPU计算机中,CPU是无法被多个程序并行使用的。

2、操作系统中存在一种调度器,它可以负责拆分CPU为一段段时间的运行片,轮流分配给不同的进程。

3、程序的运行不仅仅需要CPU,还需要很多其他资源,如内存啊,显卡啊,GPS啊,磁盘等等,这些统称为程序的执行环境,也就是程序上下文。

4、多个程序没办法同一个时间共享CPU,那怎么办呢?这个时候比进程更小的线程就出来了,通过在不同线程的切换来达到共享CPU、共享程序上下文的目的。

5、大家都知道,CPU有单核和多核区别,单核CPU其实就是多个线程会轮流得到那一个CPU核心的支持;在多核CPU中,一个核心可以服务于一个线程,例如我的电脑是4核的话,有四个线程A、B、C、D需要处理,那CPU会将他们分配到核心1、2、3、4,如果还有其他更多的线程,也必须要等待CPU的切换执行。

通过上面几个知识点,可以看出不管是在多核还是单核的系统中,CPU在多线程的环境中都是要不断切换线程来处理任务的,当然,CPU在切换任务时也不是顺便切换,而是根据一定的算法来调度、切换线程,一般有这样两种模式:分时调度和抢占式调度

分时调度就是按照顺序平均分配;

抢占式调度就是按照优先级来进行分配。

具体的算法逻辑在这里就不再详细描述,有疑问的小伙伴们可以再继续往下深入探索......

四、如何去控制多线程的执行顺序?

通过一个简单的程序,体验一下线程是否会随机被执行,手动创建5个Thread对象,然后让他们按照顺序开启,如下面代码:

/*** 多线程Test*/
public class Main {static Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("thread01");}});static Thread thread2 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("thread02");}});static Thread thread3 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("thread03");}});static Thread thread4 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("thread04");}});static Thread thread5 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("thread05");}});public static void main(String[] args) {thread1.start();thread2.start();thread3.start();thread4.start();thread5.start();}
}

最后执行的结果大家一定已经猜到了,多次执行,它的顺序并不是固定的,而是随机在改变的,例如:

e142de514c4ecf5ac5fbbafe9bae86d1.png

那么在多线程的环境中,我们有时候不想让CPU根据算法随机选取任务执行,而是想控制多线程的执行顺序,那应该如何操作呢?

目前我知道的主要有两种方法:

1、Join方法

我们直接通过在每个Thread对象后面使用join方法就可以实现线程的顺序执行,代码如下:

 public static void main(String[] args) throws Exception {thread1.start();thread1.join();thread2.start();thread2.join();thread3.start();thread3.join();thread4.start();thread4.join();thread5.start();thread5.join();}

多次执行结果都为下面这种情况:

140dc95ee551522571b69197e6c30663.png

用join方法来保证线程顺序,其实就是让main这个主线程等待子线程结束,然后主线程再执行接下来的其他线程任务,点进去join方法我们可以了解的更透彻:

 /*** Waits at most {@code millis} milliseconds for this thread to* die. A timeout of {@code 0} means to wait forever.*/public final synchronized void join(long millis)throws InterruptedException {long base = System.currentTimeMillis();long now = 0;if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");}if (millis == 0) {while (isAlive()) {wait(0);}} else {while (isAlive()) {long delay = millis - now;if (delay <= 0) {break;}wait(delay);now = System.currentTimeMillis() - base;}}}

源码中的参数millis默认值是0,从英文注释翻译后可以找到,0秒意味着永远等待,也就是thread1执行不完,那主线程你就要一直等着,一直wait,而代码中wait方法其实就是属于Object的方法,负责线程的休眠等待,当main主线程调用thread1.join的时候,main主线程会获得线程对象thread1的锁(wait 意味着拿到该对象的锁),调用该对象的wait(等待时间),直到该对象唤醒main主线程 ,比如退出后。这就意味着main 线程调用thread1.join时,必须能够拿到线程t对象的锁。

2、ExecutorService方式

首先看一下代码,我们如何通过这种方式实现线程顺序执行:

static ExecutorService executorService = Executors.newSingleThreadScheduledExecutor();public static void main(String[] args) throws Exception {executorService.submit(thread1);executorService.submit(thread2);executorService.submit(thread3);executorService.submit(thread4);executorService.submit(thread5);executorService.shutdown();}

最终的多次执行结果均为有序的,如下图:

140dc95ee551522571b69197e6c30663.png

解释一下,这种方式的原理其实就是将线程用排队的方式扔进一个线程池里,让所有的任务以单线程的模式,按照FIFO先进先出、LIFO后进先出、优先级等特定顺序执行,但是这种方式也是存在缺点的,就是当一个线程被阻塞时,其它的线程都会受到影响被阻塞,不过依然都会按照自身调度来执行,只是会存在阻塞延迟。

五、总结

总之,如果面试官真的问到大家如何控制多线程执行顺序的方法,就按照上面的两种方式回答即可,当然,面试官既然问到这个问题,就并不只是看大家是否知道这一个问题的具体答案,可能会刨根问底的让你回答更深入的一些多线程问题,所以在日常的学习过程中一定要重在积累,勤于探索,上面提到的也只是我研究到的皮毛。

最后真心希望,在以后的技术之路跟大家一起成长!

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

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

相关文章

经典sql语句50题_SQL面试经典50题:带你从建表开始

大家好&#xff0c;相信很多学习数据分析的小伙伴在面试前都经历过刷题&#xff0c;本系列小编将带大家一起来刷一刷SQL面试必会的经典50题。当然本系列文章不单单是刷题&#xff0c;小编会带着大家梳理一下解题时用到的知识点&#xff0c;所以基础比较差的小伙伴也完全不必担心…

宇视硬盘录像机onvif_视频监控系统中强大的录像机,兼容不同品牌,看看有哪些监控厂家...

选录像机&#xff0c;除路数、盘位和最大接入像素这些硬规格&#xff0c;大家还关注兼容性&#xff0c;例如好不好用、配置方不方便、使用流不流畅、录像稳不稳定等。偶尔也会有朋友问“TP的录像机可以搭配xx厂商的摄像机使用吗&#xff1f;”&#xff0c;答案当然是&#xff1…

mysql新建数据表_Mysql创建数据库与表,并添加表的数据

首先&#xff0c;在成功安装Mysql的基础上&#xff0c;进入到cmd窗口&#xff0c;登录我们的mysql管理系统。登录方式&#xff1a;mysql -u用户名 -p密码如果出现Welcome to...等一大堆英文指示&#xff0c;则说明成功进入了mysql系统。因为我们不知道系统中有哪些库&#xff0…

mysql数据库管理维护_(转)Mysql数据库管理 表的维护

原文&#xff1a;http://t.dbdao.com/archives/mysql%E6%95%B0%E6%8D%AE%E5%BA%93%E7%AE%A1%E7%90%86-%E8%A1%A8%E7%9A%84%E7%BB%B4%E6%8A%A4.html一、目标完成本课程后&#xff0c;你应该能够&#xff1a;认识不同类型的表维护操作执行维护表的SQL语句使用客户端和实用程序维护…

mysql如何使用事件_MySQL事件的使用详解

在系统管理或者数据库管理中&#xff0c;经常要周期性的执行某一个命令或者SQL语句。这个时候就会用到mysql事件&#xff0c;使用这个功能必须保证是mysql的版本是5.1以上。1.首先要查看事件是否开启了&#xff0c;SHOW VARIABLES LIKE event_scheduler;SELECT event_scheduler…

requests由于系统缓冲区空间不足_系统C盘满了空间不足的扩容?

今天带来的是无需重新分区&#xff0c;无需删除逻辑分区的C盘容量扩大方法不重新或删除分区这就意味着无需重新安装软件、游戏等程序&#xff0c;也不用备份重要资料等操作&#xff0c;不影响电脑其它盘。统C盘满了是十分常见的问题&#xff0c;由于起初对C盘分区分的太小了&am…

python3+requests+unittest_python3+requests+unittest:接口自动化测试(一)

简单介绍框架的实现逻辑&#xff0c;参考代码的git地址&#xff1a;1.环境准备python3 pycharm编辑器2.框架目录展示(该套代码只是简单入门&#xff0c;有兴趣的可以不断后期完善)(1)run.py主运行文件&#xff0c;运行之后可以生成相应的测试报告&#xff0c;并以邮件形式发送…

mysql类似于excel的删除重复项_Excel去除重复项的三种常用技巧

在工作中&#xff0c;经常会有需要在对原始记录清单进行整理时&#xff0c;剔除其中一些重复项。所谓的重复项&#xff0c;通常是指某些记录在各个字段中都有相同的内容(纵向称为字段&#xff0c;横向称为记录)&#xff0c;例如下面图中的第三行数据记录和第五行数据记录就是完…

cas java单点登录_java单点登录系统CAS的简单使用

http://blog.csdn.net/yunye114105/article/details/7997041参考&#xff1a;http://blog.csdn.net/diyagea/article/details/50638737(配置SSL协议)http://www.cnblogs.com/notDog/p/5264666.html背景有几个相对独立的java的web应用系统&#xff0c; 各自有自己的登陆验证功能…

md5 java 工具类_Java实现一个MD5工具类

I. MD5工具类利用JDK封装一个简易的MD5工具类&#xff0c;逻辑比较简单&#xff0c;直接贴下具体实现public static String getMD5(String content) {String result "";try {MessageDigest md MessageDigest.getInstance("md5");md.update(content.getBy…

java class private_Java class 中public、protected 、friendly、private的区别

转载自&#xff1a;http://hi.baidu.com/ceoct/item/7e136a2417ba6f896f2cc33cJava class 中public、protected 、friendly、private的区别1.类(class)可见性修饰符&#xff1a; public—在所有类中可见&#xff0c;在其他包中可以用import导入。缺省—就是没有修饰符&#xff…

Java 将byte转换kb_【Java】把字节数B转化为KB、MB、GB的方法

在文件处理的系统中&#xff0c;很容易就能通过一些系统自带的方法取出其大小&#xff0c;问题是这个大小往往只是一个字节数B。如果要把这个字节数转化为KB、MB、GB的最终呈现给用户&#xff0c;则涉及到整除与取余的算术运算。方法如下&#xff1a;public static String getP…

java 常见 错误_JAVA几个常见错误简析

JAVA几个常见错误简析Java看起来设计得很像C&#xff0c;但是为了使语言小和容易熟悉&#xff0c;设计者们把C语言中许多可用的特征去掉了&#xff0c;这些特征是一般程序员很少使用的。下面就来和小编一起看看JAVA几个常见错误简析吧。1、空指针错误 java.lang.NullPointerExc…

Java中文乱码破碎重组_总结彻底解决Spring MVC+Mybatis中文乱码问题

Java对于新手最容易出现的问题就是中文乱码的问题。今天我就来总结一下彻底解决Spring mvcMybatis中文乱码的方案。首先要看打一断点看一下Controller接收到参数值是否正常。如果不正常多半是因为Spring或者页面编码的设置问题。一、Spring或页面编码问题在JSP页面第一行加上下…

Java8的产品周期_java8的时间和`Date`的对比

java8提供了新的时间接口。相对Date,Calendar,个人感觉最大的好处是对时间操作的学习成本很低&#xff0c;比Calendar低。1. LocalDate,LocalTime,LocalDateTimeLocalDate 代表日期&#xff0c;LocalTime表示时刻&#xff0c;类似11:23这样的时刻。 LocalDateTime就是前面2个的…

java cpu高_Java中的CPU占用高和内存占用高的问题排查

下面通过模拟实例分析排查Java应用程序CPU和内存占用过高的过程。如果是Java面试&#xff0c;这2个问题在面试过程中出现的概率很高&#xff0c;所以我打算在这里好好总结一下。1、Java CPU过高的问题排查举个例子&#xff0c;如下&#xff1a;package com.classloading;public…

solr 时间转成java_solr时区设置解决时间多8小时问题

今天发现我博客里文章的时间与实际时间差了8小时&#xff0c;正好比原来时间多出8小时&#xff0c;想想应该是时区不一致的问题。用solr web工具查询出来的数据原样&#xff1a;"create_time": "2018-01-18T11:22:06Z"{"original": 1,"crea…

php变量原格式输出,PHP格式化输出打印变量

PHP 常用的输入变量函数print_r&#xff0c;但是输出没有换行&#xff0c;看起来很费力 我们可以自定义一个函数来实现变量的格式化输出&#xff0c;代码如下&#xff1a; function dump($vars, $label , $return false) {if (ini_get(html_errors)) {$content "\n&quo…

JAVA语言写的,用java语言写

Caballariistatic int argument;static int count0;public static void main(String[] args){argument5;printResult("",argument);System.out.println("总共"count"种");}public static void printResult(String result,int left){if(left1){Sy…

php语录网站,杨泽业:给你的wordpress博客添加经典语录功能,适合所有php网站

我们做一个网站就是要不断给访客提供最佳的用户体验&#xff0c;同时提升自己的品牌价值&#xff0c;而文章结束的经典语录的功能&#xff0c;完全可以实现我们所要达到的效果。(如下图)给你的wordpress博客添加经典语录功能这个是怎样实现的呢&#xff1f;其实很简单&#xff…