1.读写锁
读写锁: 读 与 写 互斥 ,读 与 读 共享 ,写 与 写 互斥
package com.yg.threadpool.com.yg.blockQueue;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/*
独占锁(写锁):一次只能被一个线程占有
共享锁(读锁):该锁可以被多个线程占有!
*/
public class ReadWriteLockDemo {
private ReadWriteLock rdl = new ReentrantReadWriteLock();
private static ConcurrentHashMap<String,String> chm = new ConcurrentHashMap<>();
public static void main(String[] args) {
// 写
for (int i = 1; i <= 5; i++) {
final int tempInt = i;
new Thread(()->{
chm.put(tempInt+"",tempInt+"");
},String.valueOf(i)).start();
}
// 读
for (int i = 1; i <= 5; i++) {
final int tempInt = i;
new Thread(()->{
chm.get(tempInt+"");
},String.valueOf(i)).start();
}
}
public void read(String key){
// 读 锁
Lock lock = rdl.readLock();;
try{
lock.lock();
System.out.println(Thread.currentThread().getName()+"读取" + key);
Object o = chm.get(key);
System.out.println(Thread.currentThread().getName()+"读取结果:"+o);
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void write(String key,String value){
// 写锁
Lock lock = rdl.writeLock();
try{
lock.lock();
rdl.writeLock();
System.out.println(Thread.currentThread().getName()+"写入" + key);
chm.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入ok" );
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
2.阻塞队列
2.1 队列基本介绍
队列基本类图如下:
由类图可知,Queue 下 有 Deque(双端队列) 和 BlockingQueue(阻塞队列)两个子接口;
2.2 BlockingQueue 阻塞队列
BlockingQueue 方法有四种形式:
编号 | 插入元素 | 返回元素(取,删) | 返回结果(是否报错,是否异常) | 检查 |
---|---|---|---|---|
1 | add() | remove() | 队列满,add()报错;队列空 remove 报错 | element() |
2 | offer(e) | poll() | 队列满,offer不报错 ; 队列空,取值不报错 | peek() |
3 | offer(E e, long timeout, TimeUnit unit) | poll(long,TimeUnit) | 会阻塞一段时间返回true,false结果 | |
4 | put() | take() | take()阻塞,put()阻塞 |
element() 与 peek() 方法都是返回队首元素,不同的是 element()会在队列为空的时候报异常,peek会返回null
2.2.1 add , remove 效果演示:
package com.yg.threadpool.com.yg.blockQueue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockQueueDemo {
public static void main(String[] args) {
BlockingQueue blockingQueue = new ArrayBlockingQueue(3);
/**
* 长度为3 的 队列中 ,添加元素,
* add()方法返回 boolean类型,队列满了执行 add操作 会报异常:java.lang.IllegalStateException: Queue full
* remove() :返回队首元素, 取元素,也就是删除元素,队列如果为空会抛异常:java.util.NoSuchElementException
*
*/
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
// System.out.println(blockingQueue.add("d"));
System.out.println(blockingQueue);
System.out.println("==============");
System.out.println(blockingQueue.remove());//
// remove() ,返回 true ,false
System.out.println(blockingQueue.remove("d"));
System.out.println(blockingQueue.remove());
// System.out.println(blockingQueue.remove());
System.out.println(blockingQueue);
}
}
2.2.2 offer,poll 代码示例
package com.yg.threadpool.com.yg.blockQueue;
import java.sql.Time;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* offer(e,long timeout,TimeUnit unit): 与offer的区别是,队列满了,会等待一个超时时间后进行,返回false
*
* poll(long timeOut,TimeUnit unit) ,与 poll 区别是 ,队列空了之后,会等待一个超时时间后返回null
*/
public class BlockQueueDemo {
public static void main(String[] args) {
BlockingQueue blockingQueue = new ArrayBlockingQueue(2);
// 延時1秒
try {
// 只有队列满了的时候才会 根据超时时间去尝试放元素,所以 此处并不会阻塞
System.out.println(blockingQueue.offer("a",5L, TimeUnit.SECONDS));
System.out.println(blockingQueue.offer("b"));
// 此处因为 队列已满 ,所以会阻塞 5秒,尝试去放元素
System.out.println(blockingQueue.offer("c",5L, TimeUnit.SECONDS));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
System.out.println("第一次取值为:"+blockingQueue.poll());
try {
TimeUnit.SECONDS.sleep(2);//睡2秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第2次取值为:"+blockingQueue.poll());
System.out.println("======准备取第3次的值=====");
try {
System.out.println("第3次取值取出的值为:"+blockingQueue.poll());
System.out.println("第4次取值,尝试3秒后,取出的值为:"+blockingQueue.poll(3L, TimeUnit.SECONDS));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(blockingQueue);
}
}
执行结果如下:
2.2.3 put ,take 效果演示
长度为2的阻塞队列,在添加第三个元素的时候,put()方法会阻塞,直到signal 唤醒;
package com.yg.threadpool.com.yg.blockQueue;
import java.sql.Time;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* put: 队列满了之后,会阻塞 ,直到 take()之后进行 唤醒操作
* take: 队列为空之后,会阻塞, 知道put()添加了元素之后 进行了唤醒操作
*
* poll(long timeOut,TimeUnit unit) ,与 poll 区别是 ,队列空了之后,会等待一个超时时间后返回null
*/
public class BlockQueueDemo {
public static void main(String[] args) {
BlockingQueue blockingQueue = new ArrayBlockingQueue(2);
try {
System.out.println("开始放a");
blockingQueue.put("a");
System.out.println("a完成,开始放b");
blockingQueue.put("b");
System.out.println("b完成,开始放c");
// 此处会阻塞
// blockingQueue.put("c");
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
System.out.println("第一次取值为:"+blockingQueue.take());
System.out.println("第2次取值为:"+blockingQueue.take());
System.out.println("======准备取第3次的值=====");
// 此处会阻塞,因为此时队列已经为空
System.out.println("第3次取值为:"+blockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(blockingQueue);
}
}
执行结果如下:
2.2.4 SynchronousQueue
-
其中每个插入操作必须等待另一个线程相应的删除操作,反之亦然。 同步队列没有任何内部容量,甚至没有一个容量
-
put 对应 一个take
-
队列中没有元素,即使执行了 put 操作
示例代码如下:
public class BlockQueueDemo { public static void main(String[] args) { BlockingQueue<String> blockingQueue = new SynchronousQueue<String>(); // A 存数据 new Thread(()->{ try { System.out.println(Thread.currentThread().getName() + ": put a,队列元素为"+blockingQueue); blockingQueue.put("a"); System.out.println(Thread.currentThread().getName() + ": put b,队列元素为"+blockingQueue); blockingQueue.put("b"); System.out.println(Thread.currentThread().getName() + ": put c,队列元素为"+blockingQueue); blockingQueue.put("c"); } catch (InterruptedException e) { e.printStackTrace(); } },"A").start(); // B 取数据 new Thread(()->{ try { System.out.println(Thread.currentThread().getName() +"取出:"+ blockingQueue.take()+",此时队列元素为"+blockingQueue); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName() +"取出:"+ blockingQueue.take()+",此时队列元素为"+blockingQueue); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName() +"取出:"+ blockingQueue.take()+",此时队列元素为"+blockingQueue); } catch (InterruptedException e) { e.printStackTrace(); } },"B").start(); } }
执行结果:
A: put a,队列元素为[] A: put b,队列元素为[] B取出:a,此时队列元素为[] B取出:b,此时队列元素为[] A: put c,队列元素为[] B取出:c,此时队列元素为[]
2.3 双端队列
2.3.1 双端队列基本介绍
-
支持两端元素插入和移除的线性集合
-
当使用deque作为队列时,FIFO(先进先出)。元素将添加到deque的末尾,并从头开始删除
-
插入:add , addLast ,offer ,offerLast
-
删除:remove,removeFirst , poll ,pollFirst()
-
检查:element,peek,peekFirst,getFirst
-
-
Deques也可以用作LILO(先进后出)堆栈。 这个接口应该优先于传统的
Stack
类。 当一个deque作为一个堆栈时,元素从deque的开头被推出并弹出。- 插入:push , addFirst , offerFirst
- 弹出: pop ,removeFirst , pollFirst()
2.3.2 常见的双端队列
-
ArrayDeque , ConcurrentLinkedDeque , LinkedBlockingDeque , LinkedList
-
ArrayDeque 不是并发安全的,并发修改会抛出异常 ConcurrentModificationException
-
ConcurrentLinkedDeque 线程安全
-
LinkedList 非线程安全
3.函数式接口
3.1 什么是函数式接口
函数式接口 :首先由注解 @FunctionalInterface 修饰,并且有且只有一个 抽象方法,但是可以有多个非抽象方法。
-
函数式接口可以被隐式转换为 lambda 表达式。
如:
Consumer consumer = (message)->{System.out.println("Hello " + message);}
-
JDK 1.8 新增加的函数接口:
- java.util.function 包下
如:Consumer就是一个函数式接口,
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
3.2 函数式接口分类
一共有四大函数式接口:
-
Consumer
package java.util.function; import java.util.Objects; @FunctionalInterface public interface Consumer<T> { //输入类型,无返回类型 void accept(T t); default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
-
Function
package java.util.function; import java.util.Objects; @FunctionalInterface public interface Function<T, R> { /** * Applies this function to the given argument. * * @param 输入类型 T * @return输出类型 R */ R apply(T t); default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } static <T> Function<T, T> identity() { return t -> t; } }
-
Predicate
package java.util.function; import java.util.Objects; /** * @param <T> the type of the input to the predicate * 通过传入参数类型返回 * @since 1.8 */ @FunctionalInterface public interface Predicate<T> { // 通过传入参数类型返回 boolean test(T t); default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); } default Predicate<T> negate() { return (t) -> !test(t); } default Predicate<T> or(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); } static <T> Predicate<T> isEqual(Object targetRef) { return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object); } }
-
Supplier(提供者)
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
* 只有返回,没有输入
* @return a result
*/
T get();
}
3.3 函数式接口应用
-
关于consumer应用
package com.yg.threadpool.com.yg.function; import java.util.function.Consumer; /** * 函数式接口,一共有四类 * Consumer: 只有输入,没有输出 * Function: 有输入,有输出 * Supplier: 只有输出 * Predicate: 根据输入,返回boolean类型 * */ public class FunctionDemo { public static void main(String[] args) { // lambda 写法 Consumer<String> c = message -> System.out.println("Hello " + message); // Consumer c = new Consumer <String>() { // @Override // public void accept(String o) { // System.out.println("hello:"+o); // } // }; c.accept("ccc"); } }
-
关于Supplier简单应用
package com.yg.threadpool.com.yg.function; import java.util.function.Consumer; import java.util.function.Supplier; /** * 函数式接口,一共有四类 * Consumer: 只有输入,没有输出 * Function: 有输入,有输出 * Supplier: 只有输出 * Predicate: 根据输入,返回boolean类型 * */ public class FunctionDemo { public static void main(String[] args) { // Supplier<String> supplier = new Supplier<String>() { // @Override // public String get() { // return "我是提供者supplier"; // } // }; // lambda 表达式 Supplier<String> supplier = ()-> {return "我是提供者supplier";}; System.out.println(supplier.get()); } }
-
关于function简单应用
package com.yg.threadpool.com.yg.function; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; /** * 函数式接口,一共有四类 * Consumer: 只有输入,没有输出 * Function: 有输入,有输出 * Supplier: 只有输出 * Predicate: 根据输入,返回boolean类型 * */ public class FunctionDemo { public static void main(String[] args) { Function<String,Integer> function = new Function<String,Integer>() { @Override public Integer apply(String o) { return o.length(); } }; Function<String,Integer> function1 = (str)->{return str.length();}; System.out.println(function1.apply("abc")); System.out.println(function.apply("abc")); } }
-
关于Predicate简单应用
package com.yg.threadpool.com.yg.function; import java.util.function.Predicate; /** * 函数式接口,一共有四类 * Consumer: 只有输入,没有输出 * Function: 有输入,有输出 * Supplier: 只有输出 * Predicate: 根据输入,返回boolean类型 * */ public class FunctionDemo { public static void main(String[] args) { Predicate<String> predicate = new Predicate<String>() { @Override public boolean test(String s) { // 字符串长度大于10 ,返回true if(s.length()>10){ return true; } // 否则返回false return false; } }; } }
4.stream流式计算
4.1 什么是stream
Stream(流)是一个来自数据源的元素队列并支持聚合操作
- 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
- 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
- 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
4.2 怎么生成流
在 Java 8 中, 集合接口有两个方法来生成流:
- stream() − 为集合创建串行流。
- parallelStream() − 为集合创建并行流。
package com.yg.threadpool.com.yg.stream;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
/**
* 用stream流
* 输出
* 用户中
* 年龄大于23,
* 并且是偶数
* name 大写
* 第一位位用户的姓名
*/
public class StreamDemo {
public static void main(String[] args) {
User user = new User("1","a",46);
User user1 = new User("2","b",21);
User use2 = new User("3","c",79);
User user3 = new User("4","d",27);
User user4 = new User("4","e",26);
List<User> users = Arrays.asList(user,user1, use2, user3, user4);
users.stream()
.filter((u) ->{return u.getAge()>23;})
.filter((u)->{return u.getAge()%2==0;})
.map((u)->{return u.getName().toUpperCase();})
.sorted((o1,o2)->{return o2.compareTo(o1);})
.limit(1)
.forEach(System.out::println);
}
}
5.分支合并
分支合并核心思想就是: 任务拆分,结果合并
-
ForkJoin使用场景:必须是大数据量的时候,否则没有效果。
-
工作原理: **工作窃取 ** , 底层维护了双端队列,如下图所示:
-
forkJoinTask使用规则
-
ForkJoinTask 是一个抽象类,它有三个直接子类 ,也是抽象类
-
使用forkJoinTask须 自定义类继承抽象类其中一个,重写**compute()**方法
package com.yg.threadpool.forkjoin; import lombok.Data; import java.util.concurrent.ForkJoinTask; import java.util.concurrent.RecursiveTask; /** * 自定义task 任务 * 重写了compute()方法 , * RecursiveTask 带返回值 * RecursiveAction 没有返回值 */ @Data public class ForkJoinWork extends RecursiveTask<Long> { private long start; private long end; private static final long THRESHOLD = 10000; public ForkJoinWork(long start, long end) { this.start = start; this.end = end; } /** * 求和: start 与 end 之间所有数求和 * @return */ @Override protected Long compute() { // 正常计算 if(end-start<THRESHOLD){ long sum = 0; for (long i = start; i <=end ; i++) { sum+=i; } return sum; }else{ // 分成两个任务进行计算 long middle = (start + end )/2; ForkJoinWork forkJoinWork1 = new ForkJoinWork(start,middle); forkJoinWork1.fork(); ForkJoinWork forkJoinWork2 = new ForkJoinWork(middle+1,end); forkJoinWork2.fork(); return forkJoinWork1.join() + forkJoinWork2.join(); } } }
-
使用 ForkJoinPool执行 forkJoinTask 任务 , 对比了下 普通算法,forkJoin方式 ,stream流计算效率,
-
结论:大数据量情况下,执行效率 stream > forkJoin > nomal
-
package com.yg.threadpool.forkjoin;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
public class ForkJoinDemo {
public static void main(String[] args) {
// 普通算法耗时为3255结果为:8000000002000000000
// nomalTest();
// forkJoin 耗时为2672结果为:8000000002000000000
// forkJoinTest();
// stream耗时为692结果为:8000000002000000000
streamTest();
}
// 普通 算法
public static void nomalTest(){
long start = System.currentTimeMillis();
long sum = 0;
for (long i = 1; i <=40_0000_0000L; i++) {
sum += i;
}
long end = System.currentTimeMillis();
System.out.println("普通算法耗时为"+(end-start)+"结果为:"+sum);
}
// forkJoin 算法
public static void forkJoinTest(){
// parallelism - 并行级别。 对于默认值,请使用Runtime.availableProcessors() 。
//factory - 创建新线程的工厂。 默认值为defaultForkJoinWorkerThreadFactory 。
//handler - 由于执行任务时遇到不可恢复的错误而终止的内部工作线程的处理程序。 默认值为null 。
//asyncMode - 如果为true,请为从未连接的分叉任务建立本地先进先出调度模式。 在工作线程仅处理事件式异步任务的应用程序中,此模式可能比默认的基于本地堆栈的模式更合适。 默认值为false
ForkJoinPool pool = new ForkJoinPool(Runtime.getRuntime().availableProcessors());
ForkJoinWork task = new ForkJoinWork(1L,40_0000_0000L);
// RecursiveAction
// CompleteCountedr
// RecursiveTask 递归任务
// 为异步执行给定任务的排列
// pool.execute(task);
long start = System.currentTimeMillis();
ForkJoinTask<Long> submit = pool.submit(task);
Long aLong = null;
try {
aLong = submit.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("forkJoin 耗时为"+(end-start)+"结果为:"+aLong);
}
// stream 流方式
public static void streamTest(){
long start = System.currentTimeMillis();
// reange <4 与 rangeClose <=4
// long sum = LongStream.range(1,4).parallel().reduce(0,(a,b)->{return a+b;});
long sum = LongStream.rangeClosed(1,40_0000_0000L).parallel().reduce(0,(a,b)->{return a+b;});
long end = System.currentTimeMillis();
System.out.println("stream耗时为"+(end-start)+"结果为:"+sum);
}
}
6. 异步回调
CompletableFuture 可以实现任务的异步执行
-
代码如下:
package com.yg.threadpool.forkjoin; import java.util.concurrent.*; /** * CompletableFuture */ public class ASynTest { public static void main(String[] args) { // syncSupplyStage(screenExecutor(executor), supplier); // 有返回值 CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"异步任务1"); return 100; },new ThreadPoolExecutor(1,2,1L,TimeUnit.SECONDS,new LinkedBlockingQueue<>(2))); try { System.out.println(completableFuture.thenApplyAsync((a) -> { int s = 1 / 0; System.out.println(Thread.currentThread().getName() + "执行任务2"); return 200; }).whenComplete((t, u) -> {// 执行完成后返回的参数 System.out.println("执行完成"); System.out.println("t=" + t); System.out.println("u=" + u); }).exceptionally((e) -> {// exceptionally在发生异常后才会进入执行,相当于try catch 中catch e.printStackTrace(); // 处理异常信息 System.out.println(e); return 500; }).get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println("111"); } }