1、读写锁
- 读锁(共享锁): 这个锁可以被多个线程持有!
- 写锁(独占锁):这个锁一次只能被一个线程占用!
- 读写分离,提高效率,在实际业务中去判断哪些代码是只读业务的,不要去锁这些业务
package com.coding.rwlDemo;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache cache = new MyCache();
//线程是CPU调度的(每个完整线程执行的顺序是CPU控制的)
//5个线程写入 会存在线程插队问题
for (int i = 1; i <= 5; i++) {
final String tmp=i+"";
new Thread(()->{
cache.put(tmp, UUID.randomUUID().toString().substring(0,5));
},String.valueOf(i)).start();
}
//5个线程读取
for (int i = 1; i <= 5; i++) {
final String tmp=i+"";
new Thread(()->{
cache.get(tmp);
},String.valueOf(i)).start();
}
}
}
//线程操作资源类 存在插队问题:在没有加读写锁之前,第一个线程还没写入完成,可能会存在其他写入
class MyCache{
//private Map<String,String> map = new ConcurrentHashMap<>();//为了处理安全
private Map<String,String> map = new HashMap<>();//两种情况都会存在线程插队问题
//写 独占锁(按顺序,使用Synchroninzed修饰方法也可以,只是它少了读写这种分开的概念实现)
public void put(String key,String value){
System.out.println(Thread.currentThread().getName()+"写入了"+key);
map.put(key,value);
//存在别的线程插队
System.out.println(Thread.currentThread().getName()+"写入完成");
}
//读 共享锁
public void get(String key){
System.out.println(Thread.currentThread().getName()+"读取了"+key);
map.get(key);
System.out.println(Thread.currentThread().getName()+"读取完成");
}
}
/**
* 解决:修改只能一个人修改,读可以大家一起读
*
* ReadWriteLock-->ReentrantReadWriteLock Lock不能区分读和写
* ReentrantReadWriteLock 可以区分读和写,实现更加精确的控制
*
* 多线程下尽量加锁!
*/
class MyCache2{
//private Map<String,String> map = new ConcurrentHashMap<>();//为了处理安全
private Map<String,String> map = new HashMap<>();//两种情况都会存在线程插队问题
//为了解决线程插队问题 就是我还没执行完别的线程就进来执行了
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//写 独占锁(按顺序,使用Synchroninzed修饰方法也可以,只是它少了读写这种分开的概念实现)
public void put(String key,String value){
readWriteLock.writeLock().lock();//类似 Lock.lock()
try {
System.out.println(Thread.currentThread().getName()+"写入了"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入完成");
} finally {
readWriteLock.writeLock().unlock();//类似Lock.unlock()
}
}
//读 共享锁
public void get(String key){
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"读取了"+key);
map.get(key);
System.out.println(Thread.currentThread().getName()+"读取完成");
} finally {
readWriteLock.readLock().unlock();
}
}
}
2、阻塞队列
队列:不得不阻塞 排队 FIFO 就包含:存 、取
- 队列已满必须阻塞
- 队列为空必须阻塞
为什么使用阻塞队列?
线程之间的通信,如果不关心唤醒,就是用阻塞队列
4组API
方法 | 第一组会抛出异常 | 返回一个布尔值,不会抛出异常 | 延时等待 | 一直等待 |
---|---|---|---|---|
插入 | add() | offer(e) | offer(e,time) | put() |
取出 | remove() | poll() | poll(time) | take() |
检查 | element() | peek() | - | - |
- 第一种:抛出异常
package com.coding.blockingqueqe;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class test1 {
public static void main(String[] args) {
//3是队列大小
//阻塞队列
BlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
//超出队列大小 会抛出异常提示队列已满IllegalStateException: Queue full
//System.out.println(blockingQueue.add("d"));
System.out.println(blockingQueue.element());//检查队首元素是谁
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.element());//检查队首元素是谁
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.element());//检查队首元素是谁
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());//抛出NoSuchElementException
}
}
- 第二种:返回一个布尔值,不会抛出异常
package com.coding.blockingqueqe;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class test2 {
public static void main(String[] args) {
//3是队列大小
//阻塞队列
BlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
//超出队列 不抛出异常 返回 false
//不希望抛出异常的情况下使用
System.out.println(blockingQueue.offer("d"));
System.out.println(blockingQueue.peek());//查看队首
System.out.println(blockingQueue.poll());//获取队首
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());//返回 null
System.out.println(blockingQueue.peek());//查看队首 null
}
}
3.第三种:一直阻塞
package com.coding.blockingqueqe;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class test3 {
public static void main(String[] args) throws InterruptedException {
//3是队列大小
//阻塞队列
BlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
//blockingQueue.put("d");//超过队列大小一直阻塞
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());//阻塞等待拿出元素
}
}
- 第四种:超时退出
package com.coding.blockingqueqe;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
public class test4 {
public static void main(String[] args) throws InterruptedException {
//3是队列大小
//阻塞队列
BlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
//设置超过3秒不等待
System.out.println(blockingQueue.offer("d",3L, TimeUnit.SECONDS));
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll(3L,TimeUnit.SECONDS));//null
}
}
SynchronousQueue队列
- 不存储元素,队列是空的
- 每一个put操作,必须等待一个take,否则无法继续添加元素
package com.coding.blockingqueqe;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
public class test5 {
public static void main(String[] args) {
//同步队列
//每一个put操作,必须等待一个take,否则无法继续添加元素
//不用写队列大小参数
BlockingQueue<String> queue = new SynchronousQueue<>();
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+" put a");
queue.put("a");
System.out.println(Thread.currentThread().getName()+" put b");
queue.put("b");
System.out.println(Thread.currentThread().getName()+" put c");
queue.put("c");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+queue.take());
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+queue.take());
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
}
}
3、线程池
- 三大方法、7大参数、拒绝策略、优化配置
池化技术
- 程序运行的本质:占用系统资源!提高程序的使用率,降低我们一个性能消耗(线程的创建和销毁,数据库的连接和断开都十分浪费资源,弹性访问,保证系统运行的效率)
- 线程池、连接池、内存池、对象池 …
为什么要用线程池:线程复用!!
三大方法
package com.coding.blockingqueqe;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class test6 {
public static void main(String[] args) {
// 平时我们创建一些类使用工具类操作
// 总数可以管理
//线程池 Executor原生的3大方法
//固定大小
ExecutorService threadPool1 = Executors.newFixedThreadPool(2);
//可以弹性伸缩的线程池,遇强则强(coding老师很好的比喻)
ExecutorService threadPool2 = Executors.newCachedThreadPool();
//只有一个
ExecutorService threadPool3 = Executors.newSingleThreadExecutor();
try {
//10个线程进来
for (int i = 1; i <= 10; i++) {
//线程池 执行线程
threadPool1.execute(()->{
System.out.println(Thread.currentThread().getName()+" running");
});
}
} finally {
//线程池关闭
threadPool1.shutdown();
}
}
}
- 三个方法的源码
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
ThreadPoolExecutor七大参数
//线程池最大大小 可代码级别的设置处理器数量
//Runtime.getRuntime().availableProcessors()
public ThreadPoolExecutor(int corePoolSize,//核心池子的大小
int maximumPoolSize,//池子最大大小
long keepAliveTime,//空闲线程保留时间
TimeUnit unit,
BlockingQueue<Runnable> workQueue,//队列(就是候客厅)
ThreadFactory threadFactory,//线程工厂,不修改!用来创建线程
RejectedExecutionHandler handler) {//拒绝策略
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
线程池底层业务原理
四大拒绝策略
思考:工作中怎么使用线程池?只能根据业务情况去自定义线程池的大小策略,禁止使用Executors!
最大线程池 该如何设置
CPU密集型: 根据CPU的处理器数量来定!保证最大效率
IO密集型: 50 个线程都是进程操作大io资源, 比较耗时! > 这个常用的 IO 任务数!
4、四个函数式接口
java.util.function
所有的函数式接口都可以用来简化编程模型: 都可以使用lambda表达式简化!
/**
* 函数式接口是我们现在必须要要掌握且精通的
* 4个!
* Java 8
*
* Function : 有一个输入参数有一个输出参数
* Consumer:有一个输入参数,没有输出参数
* Supplier:没有输入参数,只有输出参数
* Predicate:有一个输入参数,判断是否正确!
*/
Function
package com.coding.funcation;
import java.util.function.Function;
public class Demo01 {
public static void main(String[] args) {
//
// Function<String,Integer> function = new Function<String,Integer>() {
// @Override
// public Integer apply(String str) {
// return str.length();
// }
// };
// (参数)->{方法体}
Function<String,Integer> function = (str)->{return str.length();};
System.out.println(function.apply("cjl"));
}
}
Predicate
package com.coding.funcation;
import java.util.function.Predicate;
public class Demo02 {
public static void main(String[] args) {
// Predicate<String> predicate = new Predicate<String>() {
// @Override
// public boolean test(String str) {
// return str.isEmpty();
// }
// };
Predicate<String> predicate = str->{return str.isEmpty();};
System.out.println(predicate.test("456"));
}
}
Supplier、Consumer
package com.coding.funcation;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class Demo03 {
public static void main(String[] args) {
// Supplier<String> supplier = new Supplier<String>() {
// // 语法糖
// @Override
// public String get() {
// return "《hello,spring》";
// }
// };
Supplier<String> supplier = ()->{return "《hello,spring》";};
Consumer<String> consumer = s->{ System.out.println(s);};
// Consumer<String> consumer = new Consumer<String>() {
// @Override
// public void accept(String s) {
// System.out.println(s);
// }
// };
consumer.accept(supplier.get());
}
}
5、Stream流式计算
package com.coding.stream;
import java.util.Arrays;
import java.util.List;
// 数据库、集合 : 存数据的
// Stream:计算和处理数据交给 Stream
public class StreamDemo {
/**
* 按条件用户筛选:
* 1、id 为偶数
* 2、年龄大于24
* 3、用户名大写 映射
* 4、用户名倒排序
* 5、输出一个用户
*
* 请你只用一行代码完成!
*/
public static void main(String[] args) {
User u1 = new User(1,"a",23);
User u2 = new User(2,"b",24);
User u3 = new User(3,"c",22);
User u4 = new User(4,"d",28);
User u5 = new User(6,"e",26);
// 存储
List<User> users = Arrays.asList(u1, u2, u3, u4, u5);
// 计算等操作交给流
// forEach(消费者类型接口)
users.stream()
.filter(u->{return u.getId()%2==0;})
.filter(u->{return u.getAge()>24;})
.map(u->{return u.getName().toUpperCase();})
.sorted((o1,o2)->{return o2.compareTo(o1);})
.limit(1)
.forEach(System.out::println);
}
}
6、分支合并
什么是 forkjoin
- 任务拆分
- 结果合并
前提:forkjoin 一定是用在大数据量的情况下,否则效率反而会降低
- 工作原理:工作窃取,底层使用的双端队列
- 好处:效率高,坏处:会产生资源争夺(最后一个资源)
工作窃取
A: 任务1–>任务2–>任务3–>任务4 A领先执行完成,帮B执行任务
B: 任务1–> 任务2 任务3 任务4
package com.coding.stream;
import java.util.concurrent.ForkJoinPool;
import java.util.stream.LongStream;
public class Test {
public static void main(String[] args) {
//test1(); //15433
//test2(); //10978
test3(); //812
}
//正常计算
public static void test1(){
Long start = System.currentTimeMillis();
Long sum =0L;
for (Long i = 0L; i <= 10_0000_0000L; i++) {
sum+=i;
}
Long end = System.currentTimeMillis();
System.out.println("sum:"+sum+" time:"+(end-start));
}
//ForkJoinPool
public static void test2(){
Long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool =new ForkJoinPool();
ForkjoinDemo forkjoinDemo = new ForkjoinDemo(0L,10_0000_0000L);
Long sum = forkJoinPool.invoke(forkjoinDemo);
Long end = System.currentTimeMillis();
System.out.println("sum:"+sum+" time:"+(end-start));
}
//流计算
public static void test3(){
Long start = System.currentTimeMillis();
Long sum = LongStream.rangeClosed(0L,10_0000_1000L).parallel().reduce(0L,Long::sum);
Long end = System.currentTimeMillis();
System.out.println("sum:"+sum+" time:"+(end-start));
}
}
package com.coding.stream;
import java.util.concurrent.RecursiveTask;
public class ForkjoinDemo extends RecursiveTask<Long> {
private Long start;
private Long end;
private static final Long temp=10000L;//临界值
public ForkjoinDemo(Long start, Long end) {
this.start = start;
this.end = end;
}
//计算
@Override
protected Long compute() {
//如果这个数超过中间值 就分任务执行
if(end-start<=temp){
Long sum=0L;
for (Long i = start; i <= end; i++) {
sum+=i;
}
return sum;
}else {
Long middle=(end+start)/2;
ForkjoinDemo left = new ForkjoinDemo(start,middle);
left.fork();
ForkjoinDemo right = new ForkjoinDemo(middle+1,end);
right.fork();
//合并计算结果
return left.join()+right.join();
}
}
}
7、异步回调CompletableFuture
package com.coding.stream;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
/**
* CompletableFuture 异步回调 对将来的结果进行结果 像ajax
*/
public class CompletableFutureDemo {
public static void main(String[] args) throws Exception {
//没有返回结果:runAsync 任务执行完了就完毕了
//可以用于:新增、插入、修改不需要知道返回结果的情况下使用
// 没有返回值,好比多线程,功能更强大!
/*CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName() + " 没有返回值");
});
System.out.println(future.get());*/
//有返回结果:supplyAsync(供给型异步) 成功或者失败
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + " 有返回值");
int i=10/0;
return "1024";
});
future1.whenComplete((t,u)->{//正常编译完成
System.out.println("t===>"+t);//正常时的返回值
System.out.println("u===>"+u);//返回错误信息
}).exceptionally(e->{
System.out.println("异常信息:"+e.getMessage());
return "555";
});
System.out.println(future1.get());
}
}