线程通讯
概念:线程通讯的目标是使线程间能够互相发送信号。另一方面,线程通讯使线程能够等待其他线程的信号
wait/notify/notifyAll三种方法的使用
wait()方法是指线程调用wait方法后会释放资源处于非运行状态(等待)
notify/notifyAll方法是指线程调用该方法后,处于wait状态的线程会被唤醒重新执行,如果有多个wait,notify随机唤醒一个线程,notify唤醒所有wait线程
wait方法和sleep方法区别:
wait方法是Object提供的方法,意味着所有java类都拥有,sleep是Thread类的方法
wait方法调用后会释放线程锁,sleep则不会释放线程锁
线程池
线程池主要用来解决线程生命周期开销问题和资源不足问题
线程池至少包含的几种功能:
线程管理器:创建、销毁并管理线程池,线程池可以将工作线程放在里面,使用的时候直接通过线程池来创建线程,线程在没有任务的时候则在线程池中进行等待
工作线程:一个线程可以循环执行任务的过程,在没有任务时进行等待
任务队列:提供一种缓冲机制,如果线程池中的线程处理不过来信息的时候,则把没有处理的任务放在任务序列中
任务接口:每个任务必须实现的接口,主要用来规定任务的入口,任务执行的收尾工作、任务的执行状态等,工作线程通过改接口调度任务
信号量
信号量有时也被称为信号灯,是在多线程环境下使用的一种设施,负责协调各个线程,以保证它们能够合理、正确的使用公共资源
信号量维护了一个许可集,与同步锁相比,信号量可以控制多个线程进入执行任务,而同步锁只能控制一个线程
生产者-消费者模式
该模式主要有三个类:生产者类、消费者类、仓库类
仓库类:从面向对象编程来模拟模拟仓库,仓库应该有它自己的属性和方法。仓库最重要的属性就是容量,它必须要有的方法就是放入东西和拿出东西两个方法,有了这些属性和方法基本就可以模拟出一个简单的仓库对象了
生产者类:作为生产者类,它必须要有的方法就是生产,所以生产者类可作为一个单线程生产物品后调用仓库放入的方法把物品存入仓库,一直循环。仓库类的放入方法有非空发出可以获取物品和保证仓库容量非满的设置
消费者类:消费者类作为消费者,肯定会有消费的方法,就会去调用仓库的拿出的方法,仓库容量非满时仓库的拿出方法有可以放入物品的设置,当然消费者肯定不止一个,所以消费者类是多线程的
虽然生产者与消费者没有直接关系,但他们可以通过仓库来进行,仓库更多的是起到一个缓冲的作用
线程池调度器
Executor框架的最大优点就是把任务的提交和执行的操作在内部直接完成了,用户只需把任务说清楚就可以了,不用去管它具体是怎么去执行的
这也是对线程们的一些封装,对于设计者来说,可以让真正执行任务的线程变得不为人知;对于用户来说,提交任务加获取结果这一过程简化的就只剩下了这两步,极大的简化了过程。
Executor框架的重要核心接口和类:
Executor工具类
该类提供了一些类工厂方法用于创建线程,返回的任务执行器都实现了ExecutorService接口
如:newFixedThreadPool(int nThread) 创建固定数目数的线程池
newCachedThreadPool() 创建一个可缓存的线程池(具体查看API)
newSingleThreadExecytor() 创建一个单线程化的Executor,用唯一的工作线按照任务指定顺序(优先级)执行
newScheduledThreadPool(int corePoolSize) 创建一个支持定时即周期性的任务执行的线程池,多数情况下可用代替Timer类
ExecutorService接口
submit(Runnable) 提交一个Runnable任务用于执行,并返回一个表示该任务的Futuer,如果成功完成,Future的get方法将返回null
submit(Callable) 提交一个有返回值的任务用于执行,返回一个表示任务的未决结果的Future,任务成功完成Future的get方法将返回结果
execute(Runnable) 重用以前构造的线程
invokeAny() 如果某个指定任务成功完成,则返回其结果
invokeAll() 指定所有任务完成时,保持任务状态和结果的Future列表
shutDown() 关闭ExecutorService ,但不是立即关闭,会执行以前提交的任务,不接受新的任务。当以前的任务执行完毕才会关闭
shutDownNow() 立即关闭,尝试关闭正在执行的任务,不一定成功,可能等到该任务执行完毕,跳过已经提交但没执行的任务
Runnable与Callable的异同
两个接口都只提供了一个方法
Runnable接口提供的方法为run,无返回值;
Callable接口提供的方法为call,可以返回执行的结果
两个接口都符合函数式接口的标准
例:
public class ExecutorTest {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService service = Executors.newScheduledThreadPool(6);
for(int i=0; i<20; i++) {
Future<?> future = service.submit(() ->{
String name = Thread.currentThread().getName();
System.out.println(name+"开始执行任务");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("得到结果"+future.get());
}
service.shutdown();
}
}
锁对象:Lock接口
默认实现类ReetranrLock重入锁
有一个重要特性体现在构造器上,构造器可接受一个参数,选择是否公平锁,默认不公平锁
公平锁
先来一定先排队,先获取锁
不公平锁
不保证上述条件,非公平锁的吞吐量更大
ThreadLocal
可用来解决多线程程序并发问题
方法
set(T value) 将此线程局部变量的当前线程副本中的值设置为指定值
remvoe() 移除此线程局部变量当前线程的值
get() 获取当前线程副本中的局部变量的值
原子操作类Atomic
原子操作类相当于泛化的volatile变量,原子操作类把竞争的范围缩小到单个变量上,因此单个线程更改了变量值后都会让其他线程知道,这样就避免了线程不同步的问题