并发编程快速入门

1、线程与进程的区别

进程是所有线程的集合,每一个线程是进程中的一条执行路径。

比方:通过查看 windows 任务管理器中的列表,我们可以把运行在内存中的 exe 文件理解成进程,进程是受操作系统管理的基本运行单元。

2、为什么要使用多线程?

主要体现在多线程提高程序效率,但是需要注意,并不是使用了多线程就一定能提升性能,有的情况反而会降低性能。

多线程应用场景:

2.1、避免阻塞

我们知道,在我们单线程中,代码是顺序执行的,如果前面的操作发生了阻塞,那么就会影响到后面的操作,这时候可以采用多线程,可以理解成异步调用;其实前端里的 ajax 就是一个很好地例子,默认 ajax 是开启异步的,调用时浏览器会启一个新的线程,不阻塞当前页面的正常操作;

2.2、避免CPU空转

以http server为例,如果只用单线程响应HTTP请求,即处理完一条请求,再处理下一条请求的话,CPU会存在大量的闲置时间;

因为处理一条请求,经常涉及到RPC、数据库访问、磁盘IO等操作,这些操作的速度比CPU慢很多,而在等待这些响应的时候,CPU却不能去处理新的请求,因此http server的性能就很差;

所以很多web容器,都采用对每个请求创建新线程来响应的方式实现,这样在等待请求A的IO操作的等待时间里,就可以去继续处理请求B,对并发的响应性就好了很多 。

3、多线程常见的两种创建方式

3.1、继承Thread类,重写run方法
/*** author:  niceyoo* blog:    https://cnblogs.com/niceyoo* desc:*/
public class ThreadDemo {public static void main(String[] args) {System.out.println("-----多线程创建开始-----");/* 1.创建一个线程*/CreateThread createThread = new CreateThread();/* 2.开始执行线程 注意 开启线程不是调用run方法,而是start方法*/System.out.println("-----多线程创建启动-----");createThread.start();System.out.println("-----多线程创建结束-----");}
}class CreateThread extends Thread {/*run方法中编写 多线程需要执行的代码*/@Overridepublic void run() {for (int i = 0; i< 10; i++) {System.out.println("i:" + i);}}
}

打印结果:

-----多线程创建开始-----
-----多线程创建启动-----
-----多线程创建结束-----
i:0
i:1
i:2
i:3
i:4
i:5
i:6
i:7
i:8
i:9

线程调用 start() 方法后,代码并没有从上往下执行,而是有一条新的执行分支。

注意:画图演示多线程不同执行路径。

3.2、实现Runnable接口,重写run方法
/*** author:  niceyoo* blog:    https://cnblogs.com/niceyoo* desc:*/
class CreateRunnable implements Runnable {@Overridepublic void run() {for (int i = 0; i< 10; i++) {System.out.println("i:" + i);}}
}public class ThreadDemo2 {public static void main(String[] args) {System.out.println("-----多线程创建开始-----");/* 1.创建一个线程 */CreateRunnable createThread = new CreateRunnable();/* 2.开始执行线程 注意 开启线程不是调用run方法,而是start方法 */System.out.println("-----多线程创建启动-----");Thread thread = new Thread(createThread);thread.start();System.out.println("-----多线程创建结束-----");}
}

打印结果:

-----多线程创建开始-----
-----多线程创建启动-----
-----多线程创建结束-----
i:0
i:1
i:2
i:3
i:4
i:5
i:6
i:7
i:8
i:9
使用继承Thread类还是使用实现Runnable接口好?

使用实现Runnable接口好,继承方式的扩展性不强,java总只支持单继承,如果一个类继承Thread就不能继承其他的类了。

4、守护线程

java 中有两种线程,一种是用户线程,一种是守护线程。

  • 用户线程:指用户自定义创建的线程,主线程停止,用户线程不会停止。

  • 守护线程:当前进程不存在或主线程停止,守护进程也会被停止。

如何使用守护线程?

只需要调用 setDaemon(true) 方法即可设置为守护线程。

/*** author:  niceyoo* blog:    https://cnblogs.com/niceyoo* desc:*/
public class DaemonThread {public static void main(String[] args) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {while (true) {try {Thread.sleep(100);} catch (Exception e) {}System.out.println("我是子线程...");}}});thread.setDaemon(true);thread.start();for (int i = 0; i < 10; i++) {try {Thread.sleep(100);} catch (Exception e) {}System.out.println("我是主线程");}System.out.println("主线程执行完毕!");}
}

运行结果:

...
我是主线程
我是子线程...
我是主线程
主线程执行完毕!

从运行结果看到,main函数执行完了,守护线程也跟着停止了。

5、多线程运行状态

线程从创建、运行到结束,总是处于下面五个状态之一:

新建状态、就绪状态、运行状态、阻塞状态以及死亡状态。

5.1、新建状态

当用new操作符创建一个线程时, 例如new Thread®,线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码

5.2、就绪状态

一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。

处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。

5.3、运行状态

当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.

5.4、阻塞状态

线程运行过程中,可能由于各种原因进入阻塞状态:

  1. 线程通过调用sleep方法进入睡眠状态;
  2. 线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
  3. 线程试图得到一个锁,而该锁正被其他线程持有;
  4. 线程在等待某个触发条件;
5.5、死亡状态

有两个原因会导致线程死亡:

  1. run方法正常退出而自然死亡,
  2. 一个未捕获的异常终止了run方法而使线程猝死。

为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用 isAlive() 方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false.

6、join()方法的作用

在多线程中也是有执行的优先级的,所谓的优先级,就是cpu是否格外关注这位小兄弟,优先级越大,自然获得的好处就越多。

当在主线程当中执行到 小弟.join() 方法时,就认为主线程应该把执行权让给 小弟。

举一个例子:

创建一个线程,如何让子线程执行完毕后,主线程才能执行呢?

public class ThreadDemo3 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 10; i++) {try {Thread.sleep(10);} catch (Exception e) {}System.out.println(Thread.currentThread().getName() + "i:" + i);}}});t1.start();/* 当在主线程当中执行到t1.join()方法时,就认为主线程应该把执行权让给t1 */t1.join();for (int i = 0; i < 10; i++) {try {Thread.sleep(10);} catch (Exception e) {}System.out.println("main" + "i:" + i);}}
}

打印结果:

Thread-0i:0
Thread-0i:1
Thread-0i:2
Thread-0i:3
Thread-0i:4
Thread-0i:5
Thread-0i:6
Thread-0i:7
Thread-0i:8
Thread-0i:9
maini:0
maini:1
maini:2
maini:3
maini:4
maini:5
maini:6
maini:7
maini:8
maini:9

7、优先级

虽然上边在介绍 join 方法时提到了优先级,但是在使用 join() 方法后,该线程却变成了完全主导,这或许并不是你想要的结果。

现代操作系统基本采用时分的形式调度运行的线程,线程分配得到的时间片的多少决定了线程使用处理器资源的多少,也对应了线程优先级这个概念。在JAVA线程中,通过一个int priority来控制优先级,范围为1-10,其中10最高,默认值为5。下面是源码(基于1.8)中关于priority的一些量和方法。

class PrioritytThread implements Runnable {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().toString() + "---i:" + i);}}
}public class ThreadDemo4 {public static void main(String[] args) {PrioritytThread prioritytThread = new PrioritytThread();Thread t1 = new Thread(prioritytThread);Thread t2 = new Thread(prioritytThread);t1.start();/* 注意设置了优先级, 不代表每次都一定会被执行。 只是CPU调度会有限分配 */t1.setPriority(10);t2.start();}
}

打印结果:

Thread[t1,10,main]---i:0
Thread[t1,10,main]---i:1
Thread[t1,10,main]---i:2
Thread[t1,10,main]---i:3
Thread[t1,10,main]---i:4
Thread[t1,10,main]---i:5
Thread[t1,10,main]---i:6
Thread[t1,10,main]---i:7
Thread[t1,10,main]---i:8
Thread[t1,10,main]---i:9
Thread[t2,5,main]---i:0
Thread[t2,5,main]---i:1
Thread[t2,5,main]---i:2
Thread[t2,5,main]---i:3
Thread[t2,5,main]---i:4
Thread[t2,5,main]---i:5
Thread[t2,5,main]---i:6
Thread[t2,5,main]---i:7
Thread[t2,5,main]---i:8
Thread[t2,5,main]---i:9

7、常见的面试题

进程与线程的区别?

答:进程是所有线程的集合,每一个线程是进程中的一条执行路径。

为什么要用多线程?

答:提高程序效率

多线程创建方式?

答:继承Thread或Runnable 接口。

使用继承Thread类还是使用实现Runnable接口好?

答:实现Runnable接口好,继承方式的扩展性不强,java总只支持单继承,如果一个类继承Thread就不能继承其他的类了。

你在哪里用到了多线程?

答:主要能体现到多线程提高程序效率。

举例:分批发送短信。

8、最后总结

我们了解了什么是线程,线程是一条执行路径,每个线程互不影响;

了解了什么是多线程,多线程在一个线程中,有多条不同的执行路径,并行执行,目的为了提高程序效率。

了解了线程创建常见的两种方式:继承Thread类实现run方法,或者实现Runnable接口。

事实上,实际开发中这两种方式并不常见,而是使用线程池来进行管理。

了解了线程的几种状态,新建、就绪、运行、阻塞、死亡。

了解了线程里面也是有优先级的,用数值1-10来记录,默认是5,最大是10,通过调用 setPriority(10) 来设置优先级,需要一提的是,并不是优先级越大就一定要先执行完,只是优先执行完的概率要大。

我创建了一个java相关的公众号,用来记录自己的学习之路,感兴趣的小伙伴可以关注一下微信公众号哈:niceyoo

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

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

相关文章

工作275:表单验证层级添加

<template><!--绑定了一个 控制是否为全屏fullscreen close-on-click-modal 是否通过点击model进行关闭 visible是否显示弹出框 close关闭按钮 --><el-dialog :title"fullTitle" width"80%" :fullscreen"false" :close-on-click-…

Python 黑帽子第二章运行截图

转载于:https://www.cnblogs.com/blankicefire/p/8796158.html

公司网页添加旺旺,状态不正确

这两天在给公司做的网站添加旺旺&#xff0c;在阿里巴巴官网生成代码之后复制到网页。想试试旺旺的状态是否正确&#xff0c;登录旺旺之后一直都是不在线状态&#xff0c;如下图&#xff1a; &#xff0c; 到官网问客服之后得到的答复如下&#xff1a; 如果您登录了阿里旺旺…

并发编程之多线程线程安全(下)

1、什么是 Volatile&#xff1f; volatile 是一个类型修饰符&#xff0c;具有可见性&#xff0c;也就是说一旦某个线程修改了该被 volatile 修饰的变量&#xff0c;它会保证修改的值会立即被更新到主存&#xff0c;当有其他线程需要读取时&#xff0c;可以立即获取修改之后的值…

工作265:v-model实现原理 自定义组件使用v-model

v-model只是一个语法糖&#xff0c;等于:valueinput&#xff0c;真正的实现靠的还是&#xff1a; &#xfeff;v-bind:绑定响应式数据&#xff0c;触发 input 事件并传递数据 (核心和重点) 1其实和下面一样的 <input :value“something” input“something $event.target…

idea中隐藏.iml文件

在创建父子工程或者聚合工程时产生的大量 .iml 文件&#xff0c;有时会对我们的操作产生干扰&#xff0c;所以&#xff0c;一般情况下&#xff0c;我们都将其隐藏掉&#xff0c;步骤如下&#xff1a; File——>settings——>Editor——>File Types——>Ignore fil…

微信小程序视频弹幕效果

这次&#xff0c;和大家一起探讨下小程序视频弹幕一、按照老规矩&#xff0c;先附上gif效果图&#xff1a;二、接下来看下官方文档API对vide说明PS&#xff1a;相关属性解析&#xff1a;danmu-list:弹幕列表 enable-danmu:是否显示弹幕 danmu-btn:弹幕按钮 controls:是否显示视…

基于Docker搭建GitLab代码管理

关于Git、SVN的优缺点就不再重复了&#xff0c;本篇主要以实际搭建为主。 1、下载镜像文件 在命令行窗口执行如下指令&#xff0c;预计下载完成4分钟。 docker pull beginor/gitlab-ce:11.0.1-ce.0注意&#xff1a; 一定要配置镜像加速&#xff0c;不然会非常非常的慢&#x…

CentOS 7安装Hadoop 3.0.0

最近在学习大数据&#xff0c;需要安装Hadoop&#xff0c;自己弄了好久&#xff0c;最后终于弄好了。网上也有很多文章关于安装Hadoop的&#xff0c;但总会遇到一些问题&#xff0c;所以把在CentOS 7安装Hadoop 3.0.0的整个过程记录下来&#xff0c;有什么不对的地方大家可以留…

Docker中Maven私服的搭建

为何用到Maven私服&#xff1f; 在实际开发中&#xff0c;项目中可能会用到第三方的jar、内部通讯的服务接口都会打入到公司的私服中。 我们从项目实际开发来看&#xff1a; 一些无法从外部仓库下载的构件&#xff0c;例如内部的项目还能部署到私服上&#xff0c;以便供其他依赖…

博客作业03--栈和队列

1.学习总结 2.PTA实验作业 题目1&#xff1a;jmu-字符串是否对称 1设计思路 for i 1 to 字符串str结束if 栈顶元素 ! str[i] 字符串不对称输出noendfor字符串对称输出yes 2代码截图 3PTA提交列表说明 前面几处错误都是因为输入用的是getchar(),后来改用gets就对了好像是因为这…

apollo报:系统出错,请重试或联系系统负责人

说明&#xff1a;基于 docker 搭建的 apollo&#xff0c;创建项目后一直报系统出错&#xff0c;请重试或联系系统负责人错误。 项目人员列表一直空白&#xff1a; 经排查是数据库配置参数不匹配&#xff0c;由于自己的虚拟机 ip 为 192.168.10.130 注意&#xff1a;修改完需要重…

关于Trie的一些算法

最近学习了一下关于Trie的一些姿势&#xff0c;感觉很实用。 终于不用每次看到字符串判重等操作就只想到hash了 关于Trie的定义&#xff0c;来自百度百科 在计算机科学中&#xff0c;Trie&#xff0c;又称前缀树或字典树&#xff0c;是一种有序树状的数据结构&#xff0c;用于保…

使用nginx搭建https服务器

最近在研究nginx&#xff0c;整好遇到一个需求就是希望服务器与客户端之间传输内容是加密的&#xff0c;防止中间监听泄露信息&#xff0c;但是去证书服务商那边申请证书又不合算&#xff0c;因为访问服务器的都是内部人士&#xff0c;所以自己给自己颁发证书&#xff0c;忽略掉…

分布式配置中心阿波罗的搭建与客户端的应用

为了统一管理微服务配置文件&#xff0c;实现动态化刷新配置文件&#xff0c;常见的两种方式为阿波罗、SpringCloudConfig&#xff0c;关于两者主要区别是&#xff1a; 阿波罗配置文件存放在数据库中&#xff0c;SpringCloudConfig存放在Git里面 一、搭建过程 本篇主要演示阿波…