文章目录
一、读写锁ReentrantReadWriteLock
概念:
- 读写锁是指为了解决在多线程的情况下多个线程同时写而且又有多个线程同时去读,造成数据没有写完就有线程来读。而读写锁就是在多个线程可以进行同时读,但是只允许一个线程写,从而保证数据的安全性。
读写锁的降级:
- 为什么叫降级呢,因为写操作的权限会高于读操作的权限,即由高到低的过程就叫降级。是指读写锁在读的时候不允许写,但是在写的时候是可以允许读的,其过程就是
先获得写锁
-->再获得读锁
-->释放写锁
-->释放读锁
。
public class ReentrantReadWriteLockDemo {
public static void main(String[] args) {
ReentrantReadWriteLock reentrantLock = new ReentrantReadWriteLock();
ReentrantReadWriteLock.WriteLock writeLock = reentrantLock.writeLock();
ReentrantReadWriteLock.ReadLock readLock = reentrantLock.readLock();
//先获得写锁
writeLock.lock();
System.out.println("先获得写锁");
//再获得读锁
readLock.lock();
System.out.println("再获得读锁");
//释放写锁
writeLock.unlock();
//释放读锁
readLock.unlock();
}
}
那能不能锁的升级呢?
//先获得读锁
readLock.lock();
System.out.println("先获得读锁");
//再获得写锁
writeLock.lock();
System.out.println("再获得写锁");
很明显我的程序停在这里了,我在读的过程又有新的线程往里面写这似乎不太合理,我读完了释放锁了然后才可以进行写的操作
二、阻塞队列BlockingQueue
概念:
- 队列的特点:
先进先出
即先进入队列的先从队列出去。那阻塞队列是指当一个线程从左边往队列中放数据时队列中是满的就会阻塞,当一个线程从队列的右边取数据的时候队列中是空的也会阻塞,这就叫阻塞队列。
分类:
名称 | 简介 |
---|---|
ArrayBlockingQueue | 基于数组、长度固定、最大长度Integer.MAX_VALUE |
LinkedBlockingQueue | 基于链表、长度固定、最大长度Integer.MAX_VALUE |
DelayQueue | 设定延迟时间,达到延迟时间才可以取出,无界阻塞队列,生产者不会阻塞但消费者会 |
PriorityBlockingQueue | 采用Comparator比较器排序,无界阻塞队列 |
SynchronousQueue | 队列中只允许一个元素,每个插入操作必须等待另一个线程的删除操作 |
LinkedTransferQueue | 基于链表的无界队列 |
LinkedBlockingDeque | 基于链接节点的可选有界阻塞双端队列。可选的容量绑定构造函数参数用作防止过度扩展的一种方式。容量(如果未指定)等于 Integer.MAX_VALUE。链接节点在每次插入时动态创建,除非这会使双端队列超出容量。 |
核心方法:
分类 | 抛异常 | 布尔值及null | 阻塞 | 超时 |
---|---|---|---|---|
新增 | add(e) | offer(e) | put(e) | offer(e,time,unit) |
删除 | remove() | poll(),队列为空返回null | take() | poll(time,unit) |
检查 | element() | peek() |
三、线程池ThreadPool
概念:
- 用来存放线程的池子,使用完一个线程再放回线程池从而达到循环利用的效果提升效率,但是创建过多的线程反而会带来额外的开支,并不能提升程序的效率可能还会降低效率。
Executors线程工具类创建线程池:
-
newFixedThreadPool
创建指定数量的线程池ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
-
newSingleThreadExecutor
创建单个线程的线程池ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
-
newCachedThreadPool
具有弹性的创建线程的线程池,来的任务多则多创建线程,任务执行完则恢复原来设定的线程数量ExecutorService executorService = Executors.newCachedThreadPool();
自定义线程池:
-
通过
Executors
工具类创建线程池时其底层都是通过new ThreadPoolExecutor()
对象来创建的 -
ThreadPoolExecutor
的七个参数参数名称 释义 int corePoolSize 核心线程数 int maximumPoolSize 最大线程数 long keepAliveTime 保持存活的时间 TimeUnit unit 时间单位 BlockingQueue workQueue 阻塞队列 ThreadFactory threadFactory 创建线程的线程工厂 RejectedExecutionHandler handler 队列满了的拒绝策略
线程池的执行流程:
实际工作中我们并不会采用默认的三种创建线程池的方式,因为他们都快有可能会导致OOM,更多的是自定义线程池。
四、Fork和Join
概念:
- Fork: 将一个大的任务拆分成多个小的任务
- Join: 将多个小的任务结果组合成一个结果
使用:
完成0~100的总和:要求两数差值小于等于10
先创建一个任务继承
RecursiveTask
class MyTask extends RecursiveTask<Integer> {
private static final Integer NUMBER = 10;
private int begin;
private int end;
private int result;
public MyTask(int begin,int end) {
this.begin = begin;
this.end = end;
}
@Override
protected Integer compute() {
if ((end-begin) <= NUMBER){
for (int i = begin; i <= end ; i++) {
result += i;
}
}else {
int middle = (begin + end) >> 1;
MyTask beginToMiddle = new MyTask(begin, middle);
MyTask middleToEnd = new MyTask(middle + 1, end);
//拆分结果
beginToMiddle.fork();
middleToEnd.fork();
//合并结果
result = beginToMiddle.join() + middleToEnd.join();
}
return result;
}
}
使用
forkJoinPool
完成任务
public class ForkAndJoin {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ForkJoinPool forkJoinPool = new ForkJoinPool();
MyTask myTask = new MyTask(0,100);
ForkJoinTask<Integer> forkJoinTask = forkJoinPool.submit(myTask);
Integer value = forkJoinTask.get();
System.out.println(value);
forkJoinPool.shutdown();
}
}