文章基于【狂神说Java】JUC并发编程最新版通俗易懂_哔哩哔哩_bilibili 视频总结而来
13. 四大函数式接口
函数式接口
只有一个方法的接口,比如runnable:
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
其目的是简化编程模型,在新版本的底层框架中大量应用
foreach(消费者类的函数式接口)
函数型接口Function
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
函数型接口有一个传入参数T,有一个返回类型R。
调用打印方法:
package com.myinterface;
import java.util.function.Function;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName MyInterface.java
* @Description 函数型接口:
* 有一个输入的参数,有一个返回值
* 可以用lambda表达式来实现
* @createTime 2022年05月05日 13:26:00
*/
public class MyInterface {
public static void main(String[] args) {
Function<String, String> function = new Function<String,String>() {
@Override
public String apply(String s) {
return s;
}
};
//lambda表达式简化
Function<String, String> function1 = (s) -> {return s;};
System.out.println(function1.apply("hello"));
String apply = function.apply("123");
System.out.println(apply);
}
}
断定型接口 Predicate
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
/**
* Returns a composed predicate that represents a short-circuiting logical
* AND of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code false}, then the {@code other}
* predicate is not evaluated.
*
* <p>Any exceptions thrown during evaluation of either predicate are relayed
* to the caller; if evaluation of this predicate throws an exception, the
* {@code other} predicate will not be evaluated.
*
* @param other a predicate that will be logically-ANDed with this
* predicate
* @return a composed predicate that represents the short-circuiting logical
* AND of this predicate and the {@code other} predicate
* @throws NullPointerException if other is null
*/
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
/**
* Returns a predicate that represents the logical negation of this
* predicate.
*
* @return a predicate that represents the logical negation of this
* predicate
*/
default Predicate<T> negate() {
return (t) -> !test(t);
}
/**
* Returns a composed predicate that represents a short-circuiting logical
* OR of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code true}, then the {@code other}
* predicate is not evaluated.
*
* <p>Any exceptions thrown during evaluation of either predicate are relayed
* to the caller; if evaluation of this predicate throws an exception, the
* {@code other} predicate will not be evaluated.
*
* @param other a predicate that will be logically-ORed with this
* predicate
* @return a composed predicate that represents the short-circuiting logical
* OR of this predicate and the {@code other} predicate
* @throws NullPointerException if other is null
*/
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
/**
* Returns a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}.
*
* @param <T> the type of arguments to the predicate
* @param targetRef the object reference with which to compare for equality,
* which may be {@code null}
* @return a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}
*/
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
断定型接口只有一个输入参数,返回一个boolean类型的参数,一般用于判断。
判断字符串是否为空:
package com.myinterface;
import java.util.function.Predicate;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName MyInterface2.java
* @Description 断定型接口
* @createTime 2022年05月05日 13:34:00
*/
public class MyInterface2 {
public static void main(String[] args) {
// 判断字符串是否为空
Predicate<String> predicate = new Predicate<String>() {
@Override
public boolean test(String o) {
return o == null || o.length() == 0;
}
};
Predicate<String> predicate2 = (s) -> {
return s == null || s.length() == 0;
};
System.out.println(predicate2.test("111"));
System.out.println(predicate.test(""));
}
}
消费型接口 Consumer
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package java.util.function;
import java.util.Objects;
/**
* Represents an operation that accepts a single input argument and returns no
* result. Unlike most other functional interfaces, {@code Consumer} is expected
* to operate via side-effects.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #accept(Object)}.
*
* @param <T> the type of the input to the operation
*
* @since 1.8
*/
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
消费型接口类似于IO中的消费者,不提供返回值,只接收参数进行消费。
使用传入参数打印:
package com.myinterface;
import java.util.function.Consumer;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName MyInterface3.java
* @Description 消费性接口
* @createTime 2022年05月05日 14:14:00
*/
public class MyInterface3 {
public static void main(String[] args) {
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
Consumer<String> consumer2 = (String s) -> {
System.out.println(s);
};
consumer2.accept("hello");
consumer.accept("hello");
}
}
供给型接口 Supplier
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package java.util.function;
/**
* Represents a supplier of results.
*
* <p>There is no requirement that a new or distinct result be returned each
* time the supplier is invoked.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #get()}.
*
* @param <T> the type of results supplied by this supplier
*
* @since 1.8
*/
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
供给型接口没有传入参数,只有返回值
package com.myinterface;
import java.util.function.Supplier;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName MyInterface4.java
* @Description 供给型
* @createTime 2022年05月05日 14:20:00
*/
public class MyInterface4 {
public static void main(String[] args) {
Supplier<String> s = new Supplier<String>() {
@Override
public String get() {
return "sdad";
}
};
System.out.println(s.get());
Supplier<Integer> supplier = ()->{
return 123;
};
System.out.println(supplier.get());
}
}
14.Stream流计算
package com.Stream;
import java.util.Arrays;
import java.util.List;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName StreamTest.java
* @Description Stream流的使用
* 一行代码实现Stream流的使用
* 现在有5个用户,筛选:
* 1. ID必须是偶数
* 2. 年龄必须大于23
* 3. 用户名转成大写字母
* 4. 用户名字母倒序
* 5. 只输出第一个用户的名字
* @createTime 2022年05月05日 14:37:00
*/
public class StreamTest {
public static void main(String[] args) {
User user1 = new User("zhangsan", 20,1);
User user2 = new User("lisi", 30,2);
User user3 = new User("wangwu", 40,3);
User user4 = new User("zhaoliu", 50,4);
List<User> list = Arrays.asList(user1, user2, user3, user4);
}
}
我们使用Stream流进行链式编程,降低代码的复杂度。
package com.Stream;
import java.util.Arrays;
import java.util.List;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName StreamTest.java
* @Description Stream流的使用
* 一行代码实现Stream流的使用
* 现在有5个用户,筛选:
* 1. ID必须是偶数
* 2. 年龄必须大于23
* 3. 用户名转成大写字母
* 4. 用户名字母倒序
* 5. 只输出第一个用户的名字
* @createTime 2022年05月05日 14:37:00
*/
public class StreamTest {
public static void main(String[] args) {
User user1 = new User("zhangsan", 20,1);
User user2 = new User("lisi", 30,2);
User user3 = new User("wangwu", 40,3);
User user4 = new User("zhaoliu", 50,4);
List<User> list = Arrays.asList(user1, user2, user3, user4);
//集合就是存储
//计算交给Stream流处理,链式编程
list.stream().filter(u->{return u.getId()%2==0;})
.filter(u->{return u.getAge()>23;})
.map(u->{return u.getName().toUpperCase();})
.sorted((u1,u2)->{return u2.compareTo(u1);})
.limit(1).forEach(System.out::println);
}
}
15. ForkJoin
分支合并
jdk 1.7 并行执行任务(大任务拆成小任务) 工作窃取
不让线程等待,维护的都是双端队列,上面下面同时取用,线程2完成后可以去窃取线程1未执行完的任务。
对于求和计算的任务:
package com.forkjion;
import java.util.concurrent.RecursiveTask;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName MyFork.java
* @Description 求和计算
* @createTime 2022年05月05日 15:06:00
*/
public class MyFork extends RecursiveTask<Long> {
private final Long start;
private final Long end;
/**
* @Description 构造方法
* @param start 开始
* @param end 结束
*/
public MyFork(Long start, Long end) {
this.start = start;
this.end = end;
}
/**
* @Description 求和计算
* @return Long 返回值
*/
@Override
protected Long compute() {
long temp = 1000000000L;
if((end-start)< temp){
long sum = 0L;
for (Long i = start; i <= end; i++) {
sum += i;
}
return sum;
}else {
//通过mid计算出中间值
long mid = (start + end) / 2;
//创建子任务1
MyFork left = new MyFork(start, mid);
// 任务压入线程队列
left.fork();
//创建子任务2
MyFork right = new MyFork(mid+1, end);
// 任务压入线程队列
right.fork();
// 合并结果
return left.join() + right.join();
}
}
}
除此之外,还有使用Stream流的解决方案:
public static void test3(Long start,Long end){
long startTime = System.currentTimeMillis();
long reduce = LongStream.rangeClosed(start, end).parallel().reduce(0L, Long::sum);
long endTime = System.currentTimeMillis();
System.out.println("Stream 耗时:"+(endTime-startTime));
System.out.println(reduce);
}
.rangeClosed表示一个(start,end], 也就是一个闭区间,不包含end的函数
.parallel() 的意思是使用并行函数
.reduce的意思是合并流的元素并产生单个值
long reduce1 = LongStream.rangeClosed(start,end).parallel().reduce(0L,(a,b)->{return a+b;});
reduce中的函数可以换成Long::sum
16.异步回调
Future :对将来的某个时间进行建模
package com.future;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName FutureTest.java
* @Description 异步调用测试
* 异步执行
* 成功回调
* 失败回调
* @createTime 2022年05月05日 16:22:00
*/
public class FutureTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//发起一个请求
CompletableFuture<Void> future= CompletableFuture.runAsync(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("异步执行");
});
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
return 200;
});
completableFuture.whenComplete((a, b) -> {
System.out.println("成功回调");
//正常的返回结果
System.out.println("a:"+a);
//错误的信息
System.out.println("b:"+b);
}).exceptionally(e -> {
e.printStackTrace();
System.out.println("失败回调");
return 500;
});
System.out.println("11111");
future.get();
completableFuture.get();
}
}
17.JMM
Volatile 是java虚拟机提供的轻量级同步机制
-
保证可见性
-
不保证原子性
-
禁止指令重排
JMM:
java内存模型,不存在的东西,概念,约定
-
线程解锁前,必须把共享变量立刻刷会主存
-
线程加锁前,必须读取主存中最新值到工作内存中
-
加锁解锁是同一把锁
线程:工作内存,主内存
线程对于内存中的数据操作并不是直接操作主内存中的数据,而是把主内存中的数据复制一份到自己的工作内存,执行引擎工作结束之后再同步到主内存中
八种操作:
-
read操作(从主存中读取变量的值)
-
load操作(加载主存中的值到线程的工作内存)
-
use操作(使用执行引擎)
-
assign操作(执行引擎返回值到工作内存)
-
write操作(工作内存中的值写入主存)
-
store操作 (主存存储值)
-
lock操作(线程加锁)
-
unlock操作(线程解锁)
内存可见性:
我们现在有两个线程,一个main线程,一个是main中启动的线程,main中的线程无限循环,main线程把值改成1之后,理应停止循环,但实际没有。
package com.violate;
import java.util.concurrent.TimeUnit;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName JMMDemo.java
* @Description 内存可见性测试
* @createTime 2022年05月05日 16:51:00
*/
public class JMMDemo {
public static int num = 0;
public static void main(String[] args) {
new Thread(()->{
while (num==0){
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
num = 1;
System.out.println("num = "+num);
}
}
简单来讲就是main中的新线程不知道main线程已经修改了主存中的值,还在用自己之前复制到工作内存的值不停地循环。 (没有及时同步)
package com.violate;
import java.util.concurrent.TimeUnit;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName JMMDemo.java
* @Description volatile关键字实现内存可见
* @createTime 2022年05月05日 16:51:00
*/
public class JMMDemo {
public static int num = 0;
volatile static int num2 = 0;
public static void main(String[] args) {
new Thread(()->{
while (num2==0){
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
num2 = 1;
System.out.println("num = "+num2);
}
}
使用 volatile 关键字可以解决,保证可见性
不保证原子性:
原子性:不可分割
线程A执行任务的时候不能被打扰,不能被分割,要么同时成功,要么同时失败
package com.violate;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName JMMDemo2.java
* @Description TODO
* @createTime 2022年05月05日 17:01:00
*/
public class JMMDemo2 {
volatile static int num = 0;
public static void add(){
num++;
}
public static void main(String[] args) {
for (int i = 0; i <20 ; i++) {
new Thread(()->{
for (int j = 0; j <1000 ; j++) {
add();
}
}).start();
}
while (Thread.activeCount() > 2){
Thread.yield();
}
System.out.println(num);
}
}
可以发现每次执行的结果都不一样,表示了不保证原子性
假设我们不用锁,怎么实现原子性?
使用原子类解决:
package com.violate;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName JMMDemo2.java
* @Description TODO
* @createTime 2022年05月05日 17:01:00
*/
public class JMMDemo2 {
volatile static AtomicInteger num = new AtomicInteger(0);
public static void add(){
// +1 使用了CAS
num.incrementAndGet();
}
public static void main(String[] args) {
for (int i = 0; i <20 ; i++) {
new Thread(()->{
for (int j = 0; j <1000 ; j++) {
add();
}
}).start();
}
while (Thread.activeCount() > 2){
Thread.yield();
}
System.out.println(num);
}
}
原子类的底层调用了Unsafe类,直接在内存中修改值 。
禁止指令重排:
指令重排: 你写的程序,计算机并不是按照你写的那样去执行的
源代码->编译器优化代码->指令并行也可能会重排->内存系统也会重排->执行
系统处理器在执行指令重排的时候,会考虑数据之间的依赖性。但是可能会因为指令重排导致问题。
volatile可以避免指令重排
内存屏障,CPU指令
-
保证特定的操作执行顺序
-
可以保证某些变量的内存可见性(volatile 实现)
volatile可以保证可见性,不能保证原子性,由于内存屏障,可以保证避免指令重排的现象