多线程技术概述
线程与进程
进程
- 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间
线程
- 是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行。一个进程最少有一个线程
- 线程实际上是在进程基础之上进一步划分,一个进程启动之后,里面的若干执行路径又可以划分为若干个进程
线程池(Executors)
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。
线程池的好处
- 降低资源消耗
- 提高响应速度
- 提高线程的可管理性
Java中的四种线程池 .ExecutorService
1.缓存线程池
1 /** 2 * 缓存线程池. 3 * (长度无限制) 4 * 执行流程: 5 * 1. 判断线程池是否存在空闲线程 6 * 2. 存在则使用 7 * 3. 不存在,则创建线程 并放入线程池, 然后使用 8 */ 9 10 ExecutorService service=Executors.newCachedThreadPool(); 11 //向线程池中 加入 新的任务 12 service.execute(new Runnable() { 13 @Override public void run(){ 14 System.out.println("线程的名称:"+Thread.currentThread().getName()); 15 } 16 }); 17 service.execute(new Runnable() { 18 @Override public void run(){ 19 System.out.println("线程的名称:"+Thread.currentThread().getName()); 20 } 21 }); 22 service.execute(new Runnable() { 23 @Override public void run(){ 24 System.out.println("线程的名称:"+Thread.currentThread().getName()); 25 } 26 });
2.定长线程池
1 /** 2 * 定长线程池. 3 * (长度是指定的数值) 4 * 执行流程: 5 * 1. 判断线程池是否存在空闲线程 6 * 2. 存在则使用 7 * 3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用 8 * 4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程 9 */ 10 ExecutorService service = Executors.newFixedThreadPool(2); 11 service.execute(new Runnable() { 12 @Override public void run() { 13 System.out.println("线程的名称:"+Thread.currentThread().getName()); 14 } 15 }); 16 service.execute(new Runnable() { 17 @Override public void run() { 18 System.out.println("线程的名称:"+Thread.currentThread().getName()); 19 } 20 });
3.单线程线程池
1 /** 2 * 效果与定长线程池 创建时传入数值1 效果一致. 3 * 单线程线程池. 4 * 执行流程: 5 * 1. 判断线程池的那个线程是否空闲 6 * 2. 空闲则使用 7 * 3. 不空闲,则等待池中的单个线程空闲后使用 8 */ 9 ExecutorService service = Executors.newSingleThreadExecutor(); 10 service.execute(new Runnable() { 11 @Override public void run() { 12 System.out.println("线程的名称:"+Thread.currentThread().getName()); 13 } 14 }); 15 service.execute(new Runnable() { 16 @Override public void run() { 17 System.out.println("线程的名称:"+Thread.currentThread().getName()); 18 } 19 });
4.周期性任务定长线程池
1 public static void main(String[] args) { 2 /** 3 * 周期任务 定长线程池. 4 * 执行流程: 5 * 1. 判断线程池是否存在空闲线程 6 * 2. 存在则使用 7 * 3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用 8 * 4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程 9 * 10 * 周期性任务执行时: 11 * 定时执行, 当某个时机触发时, 自动执行某任务 . 12 */ 13 ScheduledExecutorService service = Executors.newScheduledThreadPool(2); 14 /** 15 * 定时执行 16 * 参数1. runnable类型的任务 17 * 参数2. 时长数字 18 * 参数3. 时长数字的单位 19 */ 20 /*service.schedule(new Runnable() { 21 @Override public void run() { 22 System.out.println("俩人相视一笑~ 嘿嘿嘿"); 23 } 24 },5,TimeUnit.SECONDS);*/ 25 /** 26 * 周期执行 27 * 参数1. runnable类型的任务 28 * 参数2. 时长数字(延迟执行的时长) 29 * 参数3. 周期时长(每次执行的间隔时间) 30 * 参数4. 时长数字的单位 31 */ 32 service.scheduleAtFixedRate(new Runnable() { 33 @Override 34 public void run() { 35 System.out.println("俩人相视一笑~ 嘿嘿嘿"); 36 } 37 }, 5, 2, TimeUnit.SECONDS); 38 }
Runnable与Callable
1 /** 2 * 接口定义 3 */ 4 //Callable接口 5 public interface Callable<V> { 6 V call() throws Exception; 7 } 8 //Runnable接口 9 public interface Runnable { 10 public abstract void run(); 11 }
Callable使用步骤
1. 编写类实现Callable接口 , 实现call方法
1 class XXX implements Callable<T> { 2 @Override public <T> call() throws Exception { 3 return T; 4 } 5 }
2. 创建FutureTask对象 , 并传入第一步编写的Callable类对象
1 FutureTask<Integer> future = new FutureTask<>(callable);
3. 通过Thread,启动线程
1 new Thread(future).start();
Runnable与callable相同点
- 都是接口
- 都可以编写多线程程序
- 都采用Thread.start()启动程序
Runnable与Callable不同点
-
Runnable没有返回值;Callable可以返回执行结果
-
Callable接口的call()允许抛出异常;Runnable的run()不能抛出
Callable获取返回值
Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。