Java多线程与IO流学习笔记(三)
线程状态
线程状态概述
图示:
等待与唤醒案例
1 | /** |
Timewaiting(计时等待)
进入到timewaiting有两种方式:
- 使用sleep(long m)方法,在毫秒值结束之后,线程睡醒进入到Runnable/Blocked状态
- 使用wait(long m)方法,wait方法如果在毫秒值结束之后,还没有被notify唤醒,就会自动醒来,线程睡醒进入到Runnable/Block状态
唤醒的方法:
- void notify(),随机唤醒
- void notifyAll(),全部唤醒
等待与唤醒机制
线程间通信
概念:多个线程处理同一个资源,但是处理的动作不同。
为什么要处理线程见通信:
多个线程并发执行时,默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务,并且我们希望他们有规律的执行,那么多线程之间需要一些协调通信,一次来帮我们达到多线程共同操作一份数据。
如何保证线程间通信有效利用资源:
多个线程在处理同一个资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一个变量的使用或操作。就是多个线程在操作同一份数据时,避免对同一共享变量的争夺。也就是我们需要通过一定的手段使各个线程能有效的利用资源。而这种手段就是:等待唤醒机制。
等待唤醒机制
什么是等待与唤醒机制:
这是多个线程间的一种协作机制。谈到线程我们经常想到的是线程间的竞争(race),比如去争夺锁,但这并不是 故事的全部,线程间也会有协作机制。就好比在公司里你和你的同事们,你们可能存在在晋升时的竞争,但更多时 候你们更多是一起合作以完成某些任务。
就是在一个线程进行了规定操作后,就进入等待状态(wait()), 等待其他线程执行完他们的指定代码过后 再将 其唤醒(notify());在有多个线程进行等待时, 如果需要,可以使用 notifyAll()来唤醒所有的等待线程。
wait/notify 就是线程间的一种协作机制。
线程池
概念
其实就是一个容纳多个线程的容器,其中的线程可以反复调用,省去了频繁创建线程对象的操作,无须反复创建线程而消耗过多的资源。
线程池的使用
线程池:JDK1.5之后提供的
java.util.concurrent.Executors:线程池的工厂类,用来生成线程池。
Executors类中的静态方法:
static ExecutorService newFixedThreadPool(int nThreads)创建一个可重用固定线程数的线程池。
参数:int nThreads:创建线程池中包含的线程数量
返回值:ExecutorService接口,返回的是ExecutorService接口的实现类对象,我们可以使用ExecutorService接口接收(面向接口编程)。
java.util.concurrent.ExecutorService:线程池接口,用来从线程池中获取线程,调用start方法,执行线程任务
submit(Runnable task)提交一个Runnable任务用于执行
线程池的使用步骤:
- 使用线程池的工厂类里面提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
- 创建一个类,实现Runnable接口,重写run方法,设置线程任务
- 调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法。
Runnable类实现代码:
1 | public class RunnableImpl implements Runnable { |
线程池测试类:
1 | public class Demo01ThreadPool { |
Lambda表达式
函数式编程思想概述
在数学中,函数就是有输入两,输出量的一套计算方案,也就是“拿什么东西做什么事情“。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式的思想则尽量忽略面向对象的复杂语法–强调做什么,而不是以什么形式做。
冗余的Runnable代码
传统写法
1 | public class RunnableImpl implements Runnable{ |
1 | public class Demo01Runnable{ |
代码分析:
- Thread 类需要 Runnable 接口作为参数,其中的抽象 run 方法是用来指定线程任务内容的核心;
- 为了指定 run 的方法体,不得不需要 Runnable 接口的实现类;
- 为了省去定义一个 RunnableImpl 实现类的麻烦,不得不使用匿名内部类;
- 必须覆盖重写抽象 run 方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错;
- 而实际上,似乎只有方法体才是关键所在。
编程思想转换
我们真的希望创建一个匿名内部类对象吗?不。我们只是为了做这件事情而不得不创建一个对象。我们真正希望做 的事情是:将 run 方法体内的代码传递给 Thread 类知晓。
传递一段代码——这才是我们真正的目的。而创建对象只是受限于面向对象语法而不得不采取的一种手段方式。 那,有没有更加简单的办法?如果我们将关注点从“怎么做”回归到“做什么”的本质上,就会发现只要能够更好地达 到目的,过程与形式其实并不重要。
lambda更优写法
1 | public class Demo02Lambda { |
Lambda标准格式
Lambda省去面向对象的条条框框,格式由三个部分组成:
- 一些参数
- 一个箭头
- 一段代码
Lambda表达式的标准格式为:
(参数类型 参数名称)->(代码语句)
格式说明:
- 小括号内的语法与传统方法参数列表一致:无参数则留空;多个参数则用都好分割
- ->是新引入的语法格式,代表指向动作
- 大括号内的语法与传统方法体要求基本一致。
Lambda的使用前提
Lambda的语法非常简洁,完全没有面向对象复杂的束缚。但是使用时有几个问题需要特别注意:
- 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。
无论是JDK内置的 Runnable 、 Comparator 接口还是自定义的接口,只有当接口中的抽象方法存在且唯一 时,才可以使用Lambda。 - 使用Lambda必须具有上下文推断。 也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。
备注:有且仅有一个抽象方法的接口,称为“函数式接口”。