高端网站建设公司哪家服务态度好,第三方推广平台,网站程序下载,杭州首传网站建设公司怎么样本系列会更新我在学习juc时的笔记和自己的一些思想记录。如有问题欢迎联系。
并发编程
进程与线程
1.进程和线程的概念
程序是静态的#xff0c;进程是动态的
进程
程序由指令和数据组成#xff0c;但这些指令要运行#xff0c;数据要读写#xff0c;就必须将指令加载…本系列会更新我在学习juc时的笔记和自己的一些思想记录。如有问题欢迎联系。
并发编程
进程与线程
1.进程和线程的概念
程序是静态的进程是动态的
进程
程序由指令和数据组成但这些指令要运行数据要读写就必须将指令加载至 CPU数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理 IO 的当一个程序被运行从磁盘加载这个程序的代码至内存这时就开启了一个进程。进程就可以视为程序的一个实例。大部分程序可以同时运行多个实例进程例如记事本、画图、浏览器等也有的程序只能启动一个实例进程例如网易云音乐、360 安全卫士等
线程
一个进程之内可以分为一到多个线程。一个线程就是一个指令流将指令流中的一条条指令以一定的顺序交给 CPU 执行Java 中线程作为最小调度单位进程作为资源分配的最小单位。 在 windows 中进程是不活动的只是作为线程的容器
进程和线程对比
进程基本上相互独立的而线程存在于进程内是进程的一个子集进程拥有共享的资源如内存空间等供其内部的线程共享进程间通信较为复杂同一台计算机的进程通信称为 IPCInter-process communication。不同计算机之间的进程通信需要通过网络并遵守共同的协议例如 HTTP线程通信相对简单因为它们共享进程内的内存一个例子是多个线程可以访问同一个共享变量线程更轻量线程上下文切换成本一般上要比进程上下文切换低
2.并行和并发的概念
并发
并发能力同一时间应对多件事情的能力。
单核 cpu 下线程实际还是 串行执行 的。操作系统中有一个组件叫做任务调度器将 cpu 的时间片windows下时间片最小约为 15 毫秒分给不同的程序使用只是由于 cpu 在线程间时间片很短的切换非常快人类感觉是 同时运行的 。总结为一句话就是 微观串行宏观并行 一般会将这种 线程轮流使用 CPU 的做法称为并发 concurrent。 并行
多核 cpu下每个 核core 都可以调度运行线程这时候线程可以是并行的。
引用 Rob Pike 的一段描述
并发concurrent是同一时间应对dealing with多件事情的能力并行parallel是同一时间动手做doing多件事情的能力
例子
3.线程基本应用
应用之异步调用案例1
以调用方角度来讲如果
需要等待结果返回才能继续运行就是同步不需要等待结果返回就能继续运行就是异步
1) 设计
多线程可以让方法执行变为异步的即不要巴巴干等着比如说读取磁盘文件时假设读取操作花费了 5 秒钟如果没有线程调度机制这 5 秒 cpu 什么都做不了其它代码都得暂停...
//同步Slf4j(topic c.Sync)public class Sync { public static void main(String[] args) { FileReader.read(Constants.MP4_FULL_PATH); log.debug(do other things ...); }}
//异步Slf4j(topic c.Async)public class Async { public static void main(String[] args) { new Thread(() - FileReader.read(Constants.MP4_FULL_PATH)).start(); log.debug(do other things ...); }} 2) 结论
比如在项目中视频文件需要转换格式等操作比较费时这时开一个新线程处理视频转换避免阻塞主线程tomcat 的异步 servlet 也是类似的目的让用户线程处理耗时较长的操作避免阻塞 tomcat 的工作线程ui 程序中开线程进行其他操作避免阻塞 ui 线程
充分利用多核 cpu 的优势提高运行效率。想象下面的场景执行 3 个计算最后将计算结果汇总。
计算 1 花费 10 ms 计算 2 花费 11 ms 计算 3 花费 9 ms 汇总需要 1 ms
如果是串行执行那么总共花费的时间是 10 11 9 1 31ms但如果是四核 cpu各个核心分别使用线程 1 执行计算 1线程 2 执行计算 2线程 3 执行计算 3那么 3 个线程是并行的花费时间只取决于最长的那个线程运行的时间即 11ms 最后加上汇总时间只会花费 12ms
注意 需要在多核 cpu 才能提高效率单核仍然时是轮流执行
1) 设计
Fork(1)BenchmarkMode(Mode.AverageTime)//测试模式统计程序平均时间Warmup(iterations3)//热身三次Measurement(iterations5)//五轮测试取平均值public class MyBenchmark { static int[] ARRAY new int[1000_000_00]; static { Arrays.fill(ARRAY, 1); } Benchmark public int c() throws Exception { int[] array ARRAY; FutureTaskInteger t1 new FutureTask(()-{ int sum 0; for(int i 0; i 250_000_00;i) { sum array[0i]; } return sum; }); FutureTaskInteger t2 new FutureTask(()-{ int sum 0; for(int i 0; i 250_000_00;i) { sum array[250_000_00i]; } return sum; }); FutureTaskInteger t3 new FutureTask(()-{ int sum 0; for(int i 0; i 250_000_00;i) { sum array[500_000_00i]; } return sum; }); FutureTaskInteger t4 new FutureTask(()-{ int sum 0; for(int i 0; i 250_000_00;i) { sum array[750_000_00i]; } return sum; }); new Thread(t1).start(); new Thread(t2).start(); new Thread(t3).start(); new Thread(t4).start(); return t1.get() t2.get() t3.get() t4.get(); } Benchmark public int d() throws Exception { int[] array ARRAY; FutureTaskInteger t1 new FutureTask(()-{ int sum 0; for(int i 0; i 1000_000_00;i) { sum array[0i]; } return sum; }); new Thread(t1).start(); return t1.get(); }}
在单核的情况下多线程和单线程效率基本一致多线程会有上下文切换的耗时。
在多核的情况下多线程比单线程就会效率翻倍。
2) 结论
单核 cpu 下多线程不能实际提高程序运行效率只是为了能够在不同的任务之间切换不同线程轮流使用cpu 不至于一个线程总占用 cpu别的线程没法干活。
多核 cpu 可以并行跑多个线程但能否提高程序运行效率还是要分情况的。
有些任务经过精心设计将任务拆分并行执行当然可以提高程序的运行效率。但不是所有计算任务都能拆分参考后文的【阿姆达尔定律】也不是所有任务都需要拆分任务的目的如果不同谈拆分和效率没啥意义
IO 操作不占用 cpu只是我们一般拷贝文件使用的是【阻塞 IO】这时相当于线程虽然不用 cpu但需要一直等待 IO 结束没能充分利用线程。所以才有后面的【非阻塞 IO】和【异步 IO】优化
Java线程
1.创建和运行线程
创建线程对象
// 创建线程对象Thread t new Thread() { public void run() { // 要执行的任务 }};// 启动线程交给任务调度器分配时间片交给时间片去执行。 t.start();
例如:
方法一直接使用 Thread
Slf4j(topic c.Test1)public class Test1 { public static void test2() { Thread t new Thread(()-{ log.debug(running); }, t2); t.start(); } public static void test1() { //匿名内部类的写法 Thread t new Thread(){ Override public void run() { log.debug(running); } }; //创建线程的时候可以给其指定名称 t.setName(t1); t.start(); }}
方法二使用 Runnable 配合 Thread★
把【线程】和【任务】要执行的代码分开
Thread 代表线程Runnable 可运行的任务线程要执行的代码
Runnable源码如果接口中有FunctionalInterface注解则可以被lambda简化。如果一个接口中有多个抽象接口是没办法用lambda简化的。
FunctionalInterfacepublic interface Runnable { /** * When an object implementing interface codeRunnable/code is used * to create a thread, starting the thread causes the objects * coderun/code method to be called in that separately executing * thread. * p * The general contract of the method coderun/code is that it may * take any action whatsoever. * * see java.lang.Thread#run() */ public abstract void run();} Slf4j(topic c.Test2)public class Test2 { public static void main(String[] args) { //果接口中有FunctionalInterface注解则可以被lambda简化 Runnable r () - {log.debug(running);}; Thread t new Thread(r, t2); t.start(); }}
原理之 Thread 与 Runnable 的关系
分析 Thread 的源码理清它与 Runnable 的关系
小结
方法1 是把线程和任务合并在了一起方法2 是把线程和任务分开了用 Runnable 更容易与线程池等高级 API 配合用 Runnable 让任务类脱离了 Thread 继承体系更灵活
都是走的线程里的run方法。
方法三FutureTask 配合 Thread
FutureTask 能够接收 Callable 类型的参数用来处理有返回结果的情况
FutureTask源码分析
实现一个RunnableFuture的接口
public class FutureTaskV implements RunnableFutureV {
RunnableFuture接口又继承了Runnable和Future
public interface RunnableFutureV extends Runnable, FutureV { /** * Sets this Future to the result of its computation * unless it has been cancelled. */ void run();}
Future里有get方法返回任务执行结果 V get() throws InterruptedException, ExecutionException; /** * Waits if necessary for at most the given time for the computation * to complete, and then retrieves its result, if available. * * param timeout the maximum time to wait * param unit the time unit of the timeout argument * return the computed result * throws CancellationException if the computation was cancelled * throws ExecutionException if the computation threw an * exception * throws InterruptedException if the current thread was interrupted * while waiting * throws TimeoutException if the wait timed out */ V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
Callable源码分析
Callable可以配合FutureTask让任务执行完了将结果传给其他线程
能抛出异常
FunctionalInterfacepublic interface CallableV { /** * Computes a result, or throws an exception if unable to do so. * * return computed result * throws Exception if unable to compute a result */ V call() throws Exception;}
实例代码
// 创建任务对象 FutureTaskInteger task3 new FutureTask(() - { log.debug(hello); return 100;});// 参数1 是任务对象; 参数2 是线程名字推荐 new Thread(task3, t3).start();// 主线程阻塞同步等待 task 执行完毕的结果 Integer result task3.get(); log.debug(结果是:{}, result);
输出
19:22:27 [t3] c.ThreadStarter - hello 19:22:27 [main] c.ThreadStarter - 结果是:100
2.查看进程线程的方法
windows
任务管理器可以查看进程和线程数也可以用来杀死进程tasklist 查看进程 (tasklist | findstr java)taskkill 杀死进程 (taskkill /F /PID 280660)
linux
ps -fe 查看所有进程ps -fT -p 查看某个进程PID的所有线程kill 杀死进程top 按大写 H 切换是否显示线程top -H -p 查看某个进程PID的所有线程
Java
jps 命令查看所有 Java 进程jstack 查看某个 Java 进程PID的所有线程状态jconsole 来查看某个 Java 进程中线程的运行情况图形界面可以在windowr里直接打印
jconsole 远程监控配置
需要以如下方式运行你的 java 类
java -Djava.rmi.server.hostnameip地址 -Dcom.sun.management.jmxremote - Dcom.sun.management.jmxremote.port连接端口 -Dcom.sun.management.jmxremote.ssl是否安全连接 - Dcom.sun.management.jmxremote.authenticate是否认证 java类
不需要就false,ip地址和连接端口在输入后记得把去掉
修改 /etc/hosts 文件将 127.0.0.1 映射至主机名
如果要认证访问还需要做如下步骤
复制 jmxremote.password 文件修改 jmxremote.password 和 jmxremote.access 文件的权限为 600 即文件所有者可读写连接时填入 controlRole用户名RD密码
3.线程运行的原理
栈与栈帧
ava Virtual Machine Stacks Java 虚拟机栈
我们都知道 JVM 中由堆、栈、方法区所组成其中栈内存是给谁用的呢其实就是线程每个线程启动后虚拟机就会为其分配一块栈内存。
每个栈由多个栈帧Frame组成对应着每次方法调用时所占用的内存每个线程只能有一个活动栈帧对应着当前正在执行的那个方法
线程上下文切换Thread Context Switch
因为以下一些原因导致 cpu 不再执行当前的线程转而执行另一个线程的代码
线程的 cpu 时间片用完垃圾回收有更高优先级的线程需要运行线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法
当 Context Switch 发生时需要由操作系统保存当前线程的状态并恢复另一个线程的状态Java 中对应的概念就是程序计数器Program Counter Register它的作用是记住下一条 jvm 指令的执行地址是线程私有的
状态包括程序计数器、虚拟机栈中每个栈帧的信息如局部变量、操作数栈、返回地址等Context Switch 频繁发生会影响性能
4.线程
4.1start与run
调用run
public static void main(String[] args) { Thread t1 new Thread(t1) { Override public void run() { log.debug(Thread.currentThread().getName()); FileReader.read(Constants.MP4_FULL_PATH); } }; t1.run(); log.debug(do other things ...);}
输出:
是主线程main来调用run方法,程序仍在 main 线程运行 FileReader.read() 方法调用还是同步的
19:39:14 [main] c.TestStart - main19:39:14 [main] c.FileReader - read [1.mp4] start ...19:39:18 [main] c.FileReader - read [1.mp4] end ... cost: 4227 ms19:39:18 [main] c.TestStart - do other things ...
调用 start
t1.start();
输出:
19:41:30 [main] c.TestStart - do other things ... 19:41:30 [t1] c.TestStart - t1 19:41:30 [t1] c.FileReader - read [1.mp4] start ... 19:41:35 [t1] c.FileReader - read [1.mp4] end ... cost: 4542 ms
程序在 t1 线程运行 FileReader.read() 方法调用是异步的
小结
直接调用 run 是在主线程中执行了 run没有启动新的线程使用 start 是启动新的线程通过新的线程间接执行 run 中的代码 4.2sleep与yield
sleep
调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态阻塞其它线程可以使用 interrupt 方法打断正在睡眠的线程这时 sleep 方法会抛出 InterruptedException睡眠结束后的线程未必会立刻得到执行未必能立刻获得cpu使用权建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性