当前位置: 首页 > news >正文

中山优秀网站建设博宇娱乐网站建设

中山优秀网站建设,博宇娱乐网站建设,大气的企业网站源码,2016网站建设总结Java Web 实战 02 - 多线程基础篇 - 1一 . 认识线程1.1 概念1.1.1 什么是线程?1.1.2 为什么要有多个线程?1.1.3 进程和线程的区别(面试题)1.2 第一个多线程程序1.3 创建线程1.3.1 继承Thread类1.3.2 实现Runnable接口1.3.3 继承 Thread 类 , 使用匿名内部类1.3.4 实现 Runnab… Java Web 实战 02 - 多线程基础篇 - 1一 . 认识线程1.1 概念1.1.1 什么是线程?1.1.2 为什么要有多个线程?1.1.3 进程和线程的区别(面试题)1.2 第一个多线程程序1.3 创建线程1.3.1 继承Thread类1.3.2 实现Runnable接口1.3.3 继承 Thread 类 , 使用匿名内部类1.3.4 实现 Runnable , 使用匿名内部类1.3.5 lambda 表达式来定义任务(推荐)1.4 多线程的好处1.5 多线程的使用场景1.6 小结大家好 , 这篇文章给大家带来的是多线程相关的基础知识 , 我们先介绍一下什么是线程、创建线程的方法、多线程的好处以及使用场景等。 由于 C 站的编辑器不太好用 , 导致许多排版没能生效 , 大家可移步至这里观看 https://www.yuque.com/jialebihaitao/study/qzym2pw332lm6k7q?singleDoc# 《2. 多线程 (基础)》 感谢大家的支持~ 一 . 认识线程 1.1 概念 1.1.1 什么是线程? 线程和进程之间 , 确实有一定的联系 . 线程(Thread) : 更加轻量的进程 , 也是一种实现并发编程的方案 , 创建线程和销毁线程的时候 , 比创建进程销毁进程更加轻量 个人理解 : 线程是比进程还小的单位 , 一个进程里面有多个线程 , 每个线程分别完成自己的任务 , 他们可以同时进行 举个栗子 : 一家三口来饭店吃饭 , 可是这个时候只有老板在 , 端茶做菜忙活不过来 , 所以就把老板娘叫过来了 . 这时候 , 就有两个线程 “老板” “老板娘” 此时我们就把这种情况称为多线程将一个大任务分解成不同小任务交给不同执行流就分别排队执行。其中老板娘是老板叫来的所以老板我们一般被称为主线程 1.1.2 为什么要有多个线程? 我们先来思考个问题为什么要有多个进程 CPU 单个核心已经开发到极致了 , 要想提升算力 , 就需要用多个核心 . 就需要并发编程 引入并发编程最大的目的就是为了能够充分的利用好 CPU 的多核资源。 使用多进程这种编程模式是完全可以做到并发编程的 , 并且也能够使 CPU 多核被充分利用 但是在有些场景下会存在问题。 如果需要频繁的 创建 / 销毁 进程多进程这个时候就会比较低效。 例如你写了一个服务器程序服务器要同一时刻给很多客户提供服务那么这个时候就要用到并发编程了 典型的做法就是给每个客户端分配一个进程 , 提供一对一的服务。 客户端访问就创建客户端离开了就销毁。 但是创建 / 销毁 进程本身就是一个比较低效的操作。 创建PCB PCB 也叫 进程控制块。它的作用是操作系统表示进程的属性的结构体 , 这个结构体里就包含了一些表示进程的核心信息。 分配系统资源(尤其是内存资源) 分配资源就比较浪费时间了 这个是要在系统内核资源管理模块 , 进行一系列遍历操作的 把PCB加到内核的双向链表中 为了提高这个场景下的效率我们就引入了线程这个概念 , 线程也叫做轻量级进程。 一个线程其实是包含在进程中的。(一个进程里面可以有很多个线程) 每个线程其实也有自己的 PCB (所以一个进程里面有可能对应多个 PCB ) 同一个进程里的多个线程之间共用同一份系统资源。 这就意味着新创建的线程不必重新分配系统资源只需要复用之前的即可。 因此创建线程只需要 : 创建PCB把PCB加到内核的链表中 这就是线程相对于进程做出的重大的改进也就是线程更轻量的原因。 举个栗子吧 江南皮革厂老总生意非常好 , 他想扩充他的生意。现在有两种方案 方案一 : 再租个厂子 方案二 : 在原来的厂子基础上进行扩建。 线程是包含在进程内部的逻辑执行流。(线程可以执行一段单独的代码多个线程之间是并发举行的。) 操作系统进行调度的时候其实也是以线程为单位进行调度的。 创建线程的开销比创建进程要小销毁线程的开销也比销毁进程要小 那么如果把进程比作一座工厂 , 线程就是工厂内部的流水线 再举个栗子 : 1.1.3 进程和线程的区别(面试题) 进程是包含线程的 , 线程是在进程内部的 每个进程至少有一个线程 , 叫做主线程 每个进程有独立的虚拟地址空间 , 也有自己独立的文件描述符表 . 同一个进程的多个线程之间 , 共用这一份虚拟地址空间和文件描述符表 进程是操作系统中资源分配的基本单位 , 线程是操作系统中调度执行的基本单位 多个进程同时执行的时候 , 如果一个进程挂了 , 一般不会影响到别的进程但是同一个进程中的多个线程之间 , 如果一个线程挂了 , 就很有可能把整个进程带走 , 同一个进程中的其他线程也就没了 1.2 第一个多线程程序 即使是一个最简单的Hello World程序 , 其实在运行的时候 , 也涉及到线程了 . public class Main {public static void main(String[] args) {System.out.println(Hello World);} }虽然在上述代码中 , 我们并没有手动创建其他线程 但是 Java 程序在运行的时候 , 内部也会创建出多个线程 . 一个进程里面至少会有一个线程 , 运行这个程序 , 操作系统就会创建出一个 Java 进程 , 在这个 Java 进程里面就会有一个线程调用 main 方法 谈到多进程 , 经常会谈到父进程 “子进程” 进程A里面创建了进程B A是B的父进程 , B是A的子进程 但是在多线程里面 , 没有父线程 子线程这种说法 , 即使它们之间也存在创建与被创建的关系 , 但是仍然认为线程之间地位是相等的 1.3 创建线程 1.3.1 继承Thread类 继承 Thread 类来创建一个线程类 , 然后重写里面的 run 方法 class MyThread extends Thread {Overridepublic void run() {System.out.println(Hello Mythread);} }创建 MyThread 类的实例 Thread t new MyThread();调用 start 方法启动线程 t.start();整体的代码 : class MyThread extends Thread {Overridepublic void run() {System.out.println(Hello Mythread);} } public class Demo1 {public static void main(String[] args) {// 创建一个线程// 在Java中,创建一个线程,离不开一个重要的类:Thread// 创建方式:写一个子类,继承Thread,重写里面的run方法Thread t new MyThread();//向上转型:父类类型 对象名 new 子类类型();t.start();System.out.println(Hello main);} }在 start 之前线程只是准备好了并没有真正被创建出来只有执行了 start 方法之后才在操作系统中真正创建了线程。 在操作系统中创建线程 创建 PCB把 PCB 加到链表中 那么我们来看一下运行结果 在这个代码中 , 虽然我们是先启动线程 , 再打印 “Hello main” 但是实际运行结果 , 是先打印 “Hello main” , 再打印 “Hello MyThread” 那这是怎么回事呢 ? 每个线程是独立的执行流 main 对应的线程是一个执行流 MyThread 对应的线程又是一个执行流 main 线程和 MyThread 各跑各的 , 互不影响 . 这两个执行流是 并发(并行并发) 的执行关系 此时两个线程执行的先后顺序 , 取决于操作系统调度器的具体实现 这里面的调度器里面的调度规则 , 可以简单地视为随机调度 因此我们看到的虽然是先打印 “Hello main” , 后打印 “Hello MyThread” , 但是不是一直都是这样的 . 当前看到的先打印 main , 大概率是因为受到创建线程自身的开销影响的 哪怕我们运行1000次 , main在前 , 我们也不能说第1001次的时候 , main还是在前面的 . 所以我们需要注意 : **编写多线程的代码的时候 , 默认情况下 , 代码是无序执行的 , 是操作系统随机调度的 , 所以我们不要想当然的认为多线程的执行顺序是从上到下的 . ** 但是我们还是可以影响到线程执行的先后顺序的 , 但是调度器自身的随机调度的行为修改不了 调度器依然是随机调度 , 咱们最多能做到的就是让某个线程先等待一会 , 等待另一个线程执行完了我们再去执行 那么我们再来看一下进程结束的信息 我们觉得之前运行的太快了 , 我们可以让他不结束 , 这样就可以观察一下里面都有什么线程了 class MyThread extends Thread {Overridepublic void run() {while(true) {System.out.println(Hello Mythread);}} } public class Demo1 {public static void main(String[] args) {Thread t new MyThread();t.start();while(true) {System.out.println(Hello main);}} }我们可以看到 , 程序进入死循环正在疯狂执行 , 那么我们可以使用 Java 官方提供给我们的工具来查看有哪些线程 我们找到 JDK 的安装目录 , 里面的 bin 目录有个 jconsole 工具 一进来就看到了我们的进程 , 点击连接 , 我们就能看到线程具体信息了 . 要注意的情况是 : 想要查看具体的线程信息 , 需要保证程序是一直在运行的状态 , 如果你把程序终止运行了 , 那么你就观察不到任何信息了 还有可能是正在运行但是还是什么也看不到 , 那么尝试一下用管理员方式打开再去试一下 , 应该就没问题了 . 那么刚才的程序执行的太快了 , 不方便我们观察执行结果 . 那么我们可以使用 sleep 函数 , 来让线程适当休息一下 使用 **Thread.sleep()** 的方式进行休眠 sleep 是 Thread 的静态方法 , 类名直接调用即可 sleep 函数的参数是时间 , 单位是 ms , 意思是想让线程休息多长时间 时间单位的换算 : 1 s 1000 ms 1 ms 1000 us 1 us 1000 ns 1 ns 1000 ps 进行一次网络通信 , 花的时间大概就是 us-ms 级的 进行一次读写硬盘 , 花的时间大概就是 ns-us 级的 进行一次读写内存 , 花的时间大概就是 ps - ns 级的 我们发现 , 当我们写上 sleep 函数的时候 , 报错了 . 所以我们要处理一下 , 点击报错位置 ,alt 回车 就自动把异常捕获了 , 但是 run 函数这里面只能使用 try catch语句 , 不能使用其他的捕获异常的操作 . 因为 run 函数是重写的方法 , 原函数就没有提供其他的捕获异常的方法 我们的 main 方法里面要进行异常捕获 , 就有两种方法了 , 一个是 try catch , 一个是 throws 抛出异常 , throws 是交给上一层(在这里面就是JVM)来处理 , 推荐使用 try catch 进行捕获 class MyThread extends Thread {Overridepublic void run() {while(true) {System.out.println(Hello Mythread);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}} } public class Demo1 {public static void main(String[] args) {Thread t new MyThread();t.start();while(true) {System.out.println(Hello main);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}} }查看一下运行结果 : 那么这里面就会有一个常见面试题 : 谈一谈 Thread 的 run 和 start 的区别 使用 start , 可以看到两个线程并发执行 , 两组打印交替出现 使用 run , 只打印 Hello MyThrad , 没有打印 Hello main 直接调用 run , 并没有创建出新的线程 , 只是在之前的线程中(在这里面也就是main线程) , 执行了 run 里面的内容 , 所以只打印了 Hello MyThread 使用 start , 则是创建新的线程 , 新的线程里面调用 run , 新线程和旧线程之间是并发执行的关系 1.3.2 实现Runnable接口 class MyRunnable implements Runnable {Overridepublic void run() {while(true) {System.out.println(Hello Mythread);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}} } public class Demo2 {public static void main(String[] args) {Runnable runnable new MyRunnable();Thread t new Thread(runnable);t.start();while(true) {System.out.println(Hello main);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}} }//线程本身Runnable runnable new MyRunnable(); //任务内容Thread t new Thread(runnable); //这两句代码把任务内容和线程本身给分离开了这样耦合度就降低了。 //这样的好处就是让任务的内容和线程关系不大假设这个任务不想通过多线程执行了换成别的方式执行这时候代码的改动也不会特别大那么有个问题为什么刚才使用 Thread、Runnable、Interruption 等都不需要 import 因为这几个类都在 java.lang 里面默认自动导入的 还有一种情况是不需要导包的 那就是这几个类在同一个包里面就不需要导包。 1.3.3 继承 Thread 类 , 使用匿名内部类 这个方法仍然是继承 Thread 类但是不再显式继承而是使用匿名内部类 我们之前在数据结构里面学过使用优先级队列 PriorityQueue 就可以使用 Comparable 或者 Comparator 来指定比较规则。 使用这两个接口的时候就可以使用匿名内部类的写法 public class Demo2 {public static void main(String[] args) {Thread t new Thread() {Overridepublic void run() {while(true) {System.out.println(Hello MyThread);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}};t.start();while(true) {System.out.println(Hello main);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}} }1.3.4 实现 Runnable , 使用匿名内部类 public class Demo3 {public static void main(String[] args) {Runnable runnable new Runnable() {Overridepublic void run() {while(true) {System.out.println(Hello MyThread);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}};Thread t new Thread(runnable);t.start();while(true) {System.out.println(Hello main);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}} }我们还可以这样写 public class Demo4 {public static void main(String[] args) {Thread t new Thread(new Runnable() {Overridepublic void run() {while(true) {System.out.println(Hello MyThread);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}});t.start();while(true) {System.out.println(Hello main);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}} }1.3.5 lambda 表达式来定义任务(推荐) public class Demo5 {public static void main(String[] args) {Thread t new Thread(() - {while(true) {System.out.println(Hello MyThread);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t.start();while(true) {System.out.println(Hello main);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}} }这种写法非常简洁采用了 lambda 表达式 , lambda 表达式的写法是不需要重写 run 方法的 lambda表达式其实也是一种匿名函数(只使用一次还没有名字) 像lambda表达式这种能简化代码的叫做语法糖 除了上面这5种创建线程的方式还有好几种不介绍了其实至少有7种方式来可以创建线程 对于创建线程的方式到底有哪些这也是个经典面试题了 1.4 多线程的好处 使用多线程能够充分的利用CPU多核资源 比如这个操作 public class Demo7 {private static final long num 20_0000_0000;//20亿public static void serial() {long begin_time System.currentTimeMillis();long n1 0;for (long i 0; i num; i) {n1;}long n2 0;for (long i 0; i num; i) {n2;}long end_time System.currentTimeMillis();System.out.println(单线程消耗的时间: (end_time - begin_time) ms);}public static void main(String[] args) {serial();} }我们明显可以看到单线程执行的效率是不太高的基本运行了大约2s直接就给人一个等待的感觉了。 那么我们来看看多线程的速度如何 public class Demo8 {private static final long num 20_0000_0000;public static void concurrency() {long begin_time System.currentTimeMillis();Thread t1 new Thread(() - {long a 0;//注意:a b不能在外面定义,访问不到for (long i 0; i num; i) {a;}});Thread t2 new Thread(() - {long b 0;for (long i 0; i num; i) {b;}});t1.start();t2.start();long end_time System.currentTimeMillis();System.out.println(多线程消耗的时间: (end_time - begin_time) ms);}public static void main(String[] args) {concurrency();} }多线程确实嗷嗷快。 但是有一个问题 这个代码涉及到三个进程 t1 t2 main 他们都是并发执行的谁先执行完谁后执行完是不确定的 , 很有可能 t1 t2 线程还没执行完 , main 线程就结束战斗了 . 就比如 1000m 长跑main 是裁判t1 t2 是两名运动员。t1 t2 两个兄弟在 main 的一声哨响中出发了然后main 就直接停表了 , 那么实际上测出的 t1 t2 两名运动员的成绩误差是很大的所以我们需要注意一下我们可以采取让 main 等到 t1 t2 到达终点再停止计时 . 这里我们需要用到 join 这个关键字 join关键字 是等待线程结束 在这里的意思就是等待 t1 t2 结束 , main 线程再结束 在主线程当中调用 **t1.join()** 意思就是让 main 线程等待t1执行完 所以代码应该改成这样才合理 public class Demo8 {private static final long num 20_0000_0000;public static void concurrency() {long begin_time System.currentTimeMillis();Thread t1 new Thread(() - {long a 0;for (long i 0; i num; i) {a;}});Thread t2 new Thread(() - {long b 0;for (long i 0; i num; i) {b;}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}long end_time System.currentTimeMillis();System.out.println(多线程消耗的时间: (end_time - begin_time) ms);}public static void main(String[] args) {concurrency();} }这时候的运行时间才更精确一些 我们可以把单线程的执行方式和多线程的执行方式放在一起比较一下 public class Demo9 {private static final long num 20_0000_0000;//20亿public static void serial() {long begin_time1 System.currentTimeMillis();long n1 0;for (long i 0; i num; i) {n1;}long n2 0;for (long i 0; i num; i) {n2;}long end_time1 System.currentTimeMillis();System.out.println(单线程消耗的时间: (end_time1 - begin_time1) ms);}public static void concurrency() {long begin_time System.currentTimeMillis();Thread t1 new Thread(() - {long a 0;for (long i 0; i num; i) {a;}});Thread t2 new Thread(() - {long b 0;for (long i 0; i num; i) {b;}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}long end_time System.currentTimeMillis();System.out.println(多线程消耗的时间: (end_time - begin_time) ms);}public static void main(String[] args) {serial();concurrency();} }看一下运行结果 , 多线程要比单进程快一倍左右 那么我们之前没加 join 的时候 , 多线程不是很快嘛 , 这怎么加上 join 之后咋还慢了? 加上 join 之后才是裁判等到选手到终点才停止计时。 如果没有 join 的限制main、t1、t2都是同时向下走的走的过程中调度顺序是不确定的。 最极端的情况就是一直在执行 main 线程 , t1 t2 两个线程都没有被执行 , 这样的结果肯定是不对的 有可能先执行 main 再执行 t1 再执行 t2 有可能先执行 t1再执行 t2再去执行 main再去执行 t1… 是有很多种可能性的。 那么我们继续来看 , 单线程消耗时间的时间是1095ms多线程消耗的时间是581ms那么为什么不是单线程执行时间的一半呢(即547.5s)呢 创建线程也是有开销的两个线程在CPU上不一定就是纯并发执行有可能一部分时间并行执行一部分时间并发执行线程的调度也是有开销的。 1.5 多线程的使用场景 在CPU密集型区域 代码中的大部分工作都是在使用CPU进行计算使用多线程就可以充分利用CPU多核资源可以提高效率 在IO密集型场景 I 指的是 InputO 指的是 Output 读写磁盘、读写网卡等等这些都是 IO 操作需要花费很长的时间等待但是像这种 IO 操作基本都是不消耗 CPU 就可以快速完成的工作那么这时候 CPU 就在摸鱼就可以给 CPU 找点活干 比如在食堂打饭要排队很久有的同学就拿出手机背背单词这个时候就算是等待 IO 结束我们给 CPU指定点活 1.6 小结 多线程的创建顺序是由 start 的顺序决定的 , 但是执行顺序是不确定的 , 这取决于系统的调度器怎么处理 意思就是我们先创建线程不一定就是先去执行。 举个栗子 t1.start(); t2.start(); t3.start();这样的情况就不知道谁先被执行了 , 具体线程里的任务啥时候执行 , 要看调度器 比如我跟 A B C 依次确定了关系 , 但是我不一定第一天就去跟 A 搞事情 , 跟 B/C 谁发生关系这都是不确定的 , 视心情而定 但是我们这样呢 t1.start(); t1.sleep(1000); t2.start(); t3.start();这样的意思就是我跟 A 确定关系一年了我再去跟 B C 接触 那么大概率就是先执行 A (因为都接触一年了) , 也不排除先执行 B/C (可能网恋见不了面 , 就先跟本地的 B/C 交往) 到此 , 本篇文章就结束了 , 敬请期待后续!
http://www.hkea.cn/news/14290153/

相关文章:

  • 做矿业的郑州公司网站做百度手机网站优化快
  • 做网站常用软件泗水县最新消息
  • 因脉网站建设公司怎么呀韩国怎么样优化关键词排名
  • 陕西省住房与城乡建设部网站婚纱制作网站
  • 网站建设策划书封面小程序开发工具编辑器
  • asp网站安装教程黄埔网站建设(信科网络)
  • dw网站开发环境wordpress建站打不开二级页面
  • 互联网站备案登记表wordpress页面模板是哪个文件
  • 阿里云公司网站制作微信公众号软文怎么写
  • 西安哪家做网站最好wordpress高级自定义字段怎么显示
  • 动漫网站建设毕业设计wordpress 更换语言包
  • 阿里云网站方案建设书内容管理系统开源
  • 网站seo搜索引擎优化案例网站 什么语言开发的
  • 网站右下角代码免费网页建设
  • 进口食品销售销售在那个网站做简述主要的电子商务网站有哪些
  • 做网站商丘汽车网站更新怎么做
  • 装潢建筑公司网站设计国家高新技术企业牌匾
  • 东莞网站推广渠道有哪些上海php做网站
  • 服务器哪些端口可以做网站动态的网页制作
  • 网站开发网页权限如何控制wordpress建网 打不开
  • 流量网站建设dw做的网站怎么放到服务器上
  • 做会计网站的流程WordPress自动采集翻译插件
  • 帮人做设计的网站微信息公众平台微网站建设
  • 南宁免费自助建站模板app推广80元一单
  • 南通网站制作计划上海app网络推广公司
  • flashfxp怎么上传对应网站空间搜索引擎优化要考虑哪些方面
  • 旅游网站建设标书网站建设项目的工期计划
  • 外贸订单网站推广国外创意网站设计
  • 曲阜公司网站建设价格便宜网站建设费与网络维护费区别
  • 耐克电子商务网站建设四川seo选哪家