多线程是什么?
在介绍多线程的时候,我们首先要知道什么是线程,而要了解线程还要了解进程。
1.进程:一个正在执行中的程序,每个进程执行都有一个执行顺序,该顺序是一个执行路径,或者是一个控制单元。
2.线程:进程中的一个独立控制单元,线程在控制进程的执行。一个进程中至少有一个线程。
3.多线程:一个进程中不只有一个线程。
eg:比如我们开车从北京到上海,进程可以理解为我们在去的上海的路上,坐飞机去相当于一个进程,我们不只只可以坐飞机还可坐高铁,自驾等这便是我们上面说的多线程。
原理:
多线程就是把操作系统中的这种并发执行机制原理运用在一个程序中,把一个程序划分为若干个子任务,多个子任务并发执行,每一个任务就是一个线程。
实现多线程是采用一种并发执行机制。
优势
1、方便的通信和数据交换
2、更高效地利用CPU
3、线程与进程的区别
线程 是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行. 一个进程最少 有一个线程,线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程。
进程 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间
我们为什么要用多线程呢?
为了更好地利用CPU的资源,如果只有一个线程,我们有多个任务的时候必须等着上一个任务完成才能进行。多线程则不用等待可在主线程执行的同时执行其他任务。
进程之间不可以共享数据,但是 线程可以。
系统创建进程需要为该进程重新分配系统资源,创建线程代价少。
Java语言内置了多线程功能支持,简化了Java多线程编程。
CompletionService是什么?
Callable+Future 可以实现多个task并行执行,但是如果遇到前面的task执行较慢时需要阻塞等待前面的task执行完后面task才能取得结果。
CompletionService的主要功能就是一边生成任务,一边获取任务的返回值。让两件事分开执行,任务之间不会互相阻塞,可以实现先执行完的先取结果,不再依赖任务顺序了。
他只有一个实现类:ExecutorCompletionService
CompletionService原理
内部通过阻塞队列+FutureTask,实现了任务先完成可优先获取到,即结果按照完成先后顺序排序,内部有一个先进先出的阻塞队列,用于保存已经执行完成的Future,通过调用它的take方法或poll方法可以获取到一个已经执行完成的Future,进而通过调用Future接口实现类的get方法获取最终的结果。
CompletionService的应用场景
当需要批量提交异步任务的时候建议你使用CompletionService:CompletionService将线程池Executor和阻塞队列BlockingQueue的功能融合在了一起,能够让批量异步任务的管理更简单。
CompletionService能够让异步任务的执行结果有序化:先执行完的先进入阻塞队列,利用这个特性,你可以轻松实现后续处理的有序性,避免无谓的等待,同时还可以快速实现诸如Forking Cluster这样的需求。
线程池隔离:CompletionService支持自己创建线程池,这种隔离性能避免几个特别耗时的任务拖垮整个应用的风险。
虽然使用了线程池提高了整体的执行效率,但遍历这些Future,调用Future接口实现类的get方法是阻塞的,也就是和当前这个Future关联的计算任务真正执行完成的时候,get方法才返回结果,如果当前计算任务没有执行完成,而有其它Future关联的计算任务已经执行完成了,就会白白浪费很多等待的时间,所以最好是遍历的时候谁先执行完成就先获取哪个结果,这样就节省了很多持续等待的时间。
而ExecutorCompletionService可以实现这样的效果,它的内部有一个先进先出的阻塞队列,用于保存已经执行完成的Future,通过调用它的take方法或poll方法可以获取到一个已经执行完成的Future,进而通过调用Future接口实现类的get方法获取最终的结果
ExecutorCompletionService实现了CompletionService接口,在CompletionService接口中定义了如下这些方法:
Future submit(Callable task):提交一个Callable类型任务,并返回该任务执行结果关联的Future;
Future submit(Runnable task,V result):提交一个Runnable类型任务,并返回该任务执行结果关联的Future;
Future take():从内部阻塞队列中获取并移除第一个执行完成的任务,阻塞,直到有任务完成;
Future poll():从内部阻塞队列中获取并移除第一个执行完成的任务,获取不到则返回null,不阻塞;
Future poll(long timeout, TimeUnit unit):从内部阻塞队列中获取并移除第一个执行完成的任务,阻塞时间为timeout,获取不到则返回null;
package com.example.yanwc; import java.util.Random; import java.util.concurrent.*; public class Deadlock { public static void main(String[] args) throws ExecutionException, InterruptedException { // 创建线程池 ExecutorService executor = Executors.newFixedThreadPool(5); // 创建CompletionService CompletionService<Integer> cs = new ExecutorCompletionService<>(executor); // 开启5个任务 for (int i = 0; i < 5; i++) { cs.submit(new CompletionTask()); } // 将询价结果异步保存到数据库 for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " 获取到任务结果:" + cs.take().get()); } } } /** * 自定义的任务 */ class CompletionTask implements Callable<Integer> { @Override public Integer call() throws Exception { // 获取随机的世界 int randomSleepTime = new Random().nextInt(10) * 1000; // 开始执行 System.out.println(Thread.currentThread().getName() + " 开始执行,当前任务需要等待" + randomSleepTime + "毫秒"); // 线程等待,模拟正在执行 TimeUnit.MILLISECONDS.sleep(randomSleepTime); // 结束执行 System.out.println(Thread.currentThread().getName() + "结束执行,当前任务需要等待" + randomSleepTime + "毫秒"); // 返回他等待的时间 return randomSleepTime; } }
E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\bin\java.exe -javaagent:D:\idea\idea20203pj_186103\lib\idea_rt.jar=51798:D:\idea\idea20203pj_186103\bin -Dfile.encoding=UTF-8 -classpath E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\charsets.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\deploy.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\ext\access-bridge-64.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\ext\cldrdata.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\ext\dnsns.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\ext\jaccess.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\ext\jfxrt.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\ext\localedata.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\ext\nashorn.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\ext\sunec.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\ext\sunjce_provider.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\ext\sunmscapi.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\ext\sunpkcs11.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\ext\zipfs.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\javaws.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\jce.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\jfr.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\jfxswt.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\jsse.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\management-agent.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\plugin.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\resources.jar;E:\JAVA-JIHE\JDK\java\jdk1.8.0_162\jre\lib\rt.jar;F:\IDEA_gulimall\Login_Register\yanwc\target\classes;D:\maven\apache-maven-3.5.3-bin\repository\org\springframework\boot\spring-boot-starter-thymeleaf\2.2.4.RELEASE\spring-boot-starter-thymeleaf-2.2.4.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\springframework\boot\spring-boot-starter\2.2.4.RELEASE\spring-boot-starter-2.2.4.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\springframework\boot\spring-boot\2.2.4.RELEASE\spring-boot-2.2.4.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\springframework\boot\spring-boot-autoconfigure\2.2.4.RELEASE\spring-boot-autoconfigure-2.2.4.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\springframework\boot\spring-boot-starter-logging\2.2.4.RELEASE\spring-boot-starter-logging-2.2.4.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;D:\maven\apache-maven-3.5.3-bin\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\apache\logging\log4j\log4j-to-slf4j\2.12.1\log4j-to-slf4j-2.12.1.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\apache\logging\log4j\log4j-api\2.12.1\log4j-api-2.12.1.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\slf4j\jul-to-slf4j\1.7.30\jul-to-slf4j-1.7.30.jar;D:\maven\apache-maven-3.5.3-bin\repository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\yaml\snakeyaml\1.25\snakeyaml-1.25.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\thymeleaf\thymeleaf-spring5\3.0.11.RELEASE\thymeleaf-spring5-3.0.11.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\thymeleaf\thymeleaf\3.0.11.RELEASE\thymeleaf-3.0.11.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\attoparser\attoparser\2.0.5.RELEASE\attoparser-2.0.5.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\unbescape\unbescape\1.1.6.RELEASE\unbescape-1.1.6.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\thymeleaf\extras\thymeleaf-extras-java8time\3.0.4.RELEASE\thymeleaf-extras-java8time-3.0.4.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\springframework\boot\spring-boot-starter-web\2.2.4.RELEASE\spring-boot-starter-web-2.2.4.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\springframework\boot\spring-boot-starter-json\2.2.4.RELEASE\spring-boot-starter-json-2.2.4.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\com\fasterxml\jackson\core\jackson-databind\2.10.2\jackson-databind-2.10.2.jar;D:\maven\apache-maven-3.5.3-bin\repository\com\fasterxml\jackson\core\jackson-annotations\2.10.2\jackson-annotations-2.10.2.jar;D:\maven\apache-maven-3.5.3-bin\repository\com\fasterxml\jackson\core\jackson-core\2.10.2\jackson-core-2.10.2.jar;D:\maven\apache-maven-3.5.3-bin\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.10.2\jackson-datatype-jdk8-2.10.2.jar;D:\maven\apache-maven-3.5.3-bin\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.10.2\jackson-datatype-jsr310-2.10.2.jar;D:\maven\apache-maven-3.5.3-bin\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.10.2\jackson-module-parameter-names-2.10.2.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\springframework\boot\spring-boot-starter-tomcat\2.2.4.RELEASE\spring-boot-starter-tomcat-2.2.4.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\apache\tomcat\embed\tomcat-embed-core\9.0.30\tomcat-embed-core-9.0.30.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\apache\tomcat\embed\tomcat-embed-el\9.0.30\tomcat-embed-el-9.0.30.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.30\tomcat-embed-websocket-9.0.30.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\springframework\boot\spring-boot-starter-validation\2.2.4.RELEASE\spring-boot-starter-validation-2.2.4.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\jakarta\validation\jakarta.validation-api\2.0.2\jakarta.validation-api-2.0.2.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\hibernate\validator\hibernate-validator\6.0.18.Final\hibernate-validator-6.0.18.Final.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\jboss\logging\jboss-logging\3.4.1.Final\jboss-logging-3.4.1.Final.jar;D:\maven\apache-maven-3.5.3-bin\repository\com\fasterxml\classmate\1.5.1\classmate-1.5.1.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\springframework\spring-web\5.2.3.RELEASE\spring-web-5.2.3.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\springframework\spring-beans\5.2.3.RELEASE\spring-beans-5.2.3.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\springframework\spring-webmvc\5.2.3.RELEASE\spring-webmvc-5.2.3.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\springframework\spring-aop\5.2.3.RELEASE\spring-aop-5.2.3.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\springframework\spring-context\5.2.3.RELEASE\spring-context-5.2.3.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\springframework\spring-expression\5.2.3.RELEASE\spring-expression-5.2.3.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\projectlombok\lombok\1.18.10\lombok-1.18.10.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\springframework\spring-core\5.2.3.RELEASE\spring-core-5.2.3.RELEASE.jar;D:\maven\apache-maven-3.5.3-bin\repository\org\springframework\spring-jcl\5.2.3.RELEASE\spring-jcl-5.2.3.RELEASE.jar com.example.yanwc.Deadlock pool-1-thread-4 开始执行,当前任务需要等待2000毫秒 pool-1-thread-3 开始执行,当前任务需要等待0毫秒 pool-1-thread-3结束执行,当前任务需要等待0毫秒 pool-1-thread-2 开始执行,当前任务需要等待2000毫秒 pool-1-thread-5 开始执行,当前任务需要等待2000毫秒 pool-1-thread-1 开始执行,当前任务需要等待7000毫秒 main 获取到任务结果:0 pool-1-thread-2结束执行,当前任务需要等待2000毫秒 pool-1-thread-4结束执行,当前任务需要等待2000毫秒 pool-1-thread-5结束执行,当前任务需要等待2000毫秒 main 获取到任务结果:2000 main 获取到任务结果:2000 main 获取到任务结果:2000 pool-1-thread-1结束执行,当前任务需要等待7000毫秒 main 获取到任务结果:7000
package com.example; import java.util.ArrayList; import java.util.List; import java.util.concurrent.*; public class DeadLock2 { public static void main(String[] args) throws ExecutionException, InterruptedException { Integer i = 1; Integer ii = 2; Integer iii = 3; Integer iiii = 4; List<Integer> list = new ArrayList<>(); //把相关参数传入list集合 list.add(i); list.add(ii); list.add(iii); list.add(iiii); System.out.println("转换前" + list); //线程返回对象 List<Future<Integer>> l = new ArrayList<>(); // 创建线程池等待队列 BlockingQueue q = new LinkedBlockingQueue(); //创建线程池并建立相关的参数 // 创建线程对象 开启多线程 ThreadPoolExecutor executor = new ThreadPoolExecutor(4,6,3, TimeUnit.SECONDS,q); //线程 用来执行任务 CompletionService<Integer> completionService = new ExecutorCompletionService(executor); //for循环 for (Integer integer : list) { Future<Integer> future = completionService.submit(wc(integer)); l.add(future); } List<Integer> ll = new ArrayList<>(); //实例化数据 for (Future<Integer> future : l) { ll.add(future.get()); } System.out.println("转换后" + ll); } private static Callable<Integer> wc(Integer integer) { Callable<Integer> callable = new Callable<Integer>() { @Override public Integer call() throws Exception { return integer + 1; } }; return callable; } }
运行结果:
转换前[1, 2, 3, 4] 转换后[2, 3, 4, 5]
构造方法解析:
CompletionService的构造方法源码分析
/**
传入线程池的构造方法
*/
public ExecutorCompletionService(Executor executor) { // 线程池为null,直接抛出异常 if (executor == null) throw new NullPointerException(); // 把使用的线程池赋值 this.executor = executor; // 判断是不是AbstractExecutorService的子类 this.aes = (executor instanceof AbstractExecutorService) ? (AbstractExecutorService) executor : null; // 默认只是用LinkedBlockingQueue进行结果的存放 this.completionQueue = new LinkedBlockingQueue<Future<V>>(); } /** * 传入线程池的构造方法和使用的队列 */ public ExecutorCompletionService(Executor executor, BlockingQueue<Future<V>> completionQueue) { // 线程池为null或者使用的队列为空,直接抛出异常 if (executor == null || completionQueue == null) throw new NullPointerException(); // 把使用的线程池赋值 this.executor = executor; // 判断是不是AbstractExecutorService的子类 this.aes = (executor instanceof AbstractExecutorService) ? (AbstractExecutorService) executor : null; // 使用传入的队列进行结果的存放 this.completionQueue = completionQueue; } CompletionService的submit方法源码分析 /** * CompletionService的任务提交方法 */ public Future<V> submit(Runnable task, V result) { // 任务为空,抛出空指针异常 if (task == null) throw new NullPointerException(); // 创建一个具体的任务 RunnableFuture<V> f = newTaskFor(task, result); // 执行这个任务:封装一下,重写了done方法,将运行结果放入队列中。 executor.execute(new QueueingFuture(f)); // 返回执行的结果 return f; } /** * QueueingFuture类:他重写了done方法 */ private class QueueingFuture extends FutureTask<Void> { QueueingFuture(RunnableFuture<V> task) { super(task, null); this.task = task; } // 执行完成的方法:运行结果放入队列中。 protected void done() { completionQueue.add(task); } private final Future<V> task; } CompletionService的take方法源码分析 /** * CompletionService的出队take方法:直接调用队列的阻塞出队方法。 */ public Future<V> take() throws InterruptedException { return completionQueue.take(); }