1 bean优化响应式编程
@Data//Lombok自动生成set和get方法,让代码结果简单明了
@Accessors(chain = true)//实现链式调用节省很多时间
public class TestDataVo implements Serializable {
private String name;
private String nick;
private String phone;
public static void main(String[] args) {
TestDataVo testDataVo1 = new TestDataVo().setName("afdasf").setNick("afas");
System.out.println(testDataVo1.name);
System.out.println(testDataVo1.getName());
System.out.println(testDataVo1.nick);
System.out.println(testDataVo1.getNick());
}
}
2 map装载字符串返回
package com.jeeplus.modules.sms.utils;
import java.util.HashMap;
/**
* map工具类
*
*/
public class MapUtils extends HashMap<String, Object> {
/**
* 链式调用装载键值对
* @param key
* @param value
* @return
*/
public MapUtils put(String key, Object value) {
super.put(key, value);
return this;
}
public static void main(String[] args) {
MapUtils utils=new MapUtils();
MapUtils put = utils.put("1", "2").put("15", "2");
//iterating over keys only
for (Object key : put.keySet()) {
System.out.println("Key = " + key);
}
}
}
3 for循环操作数据库(多线程安全处理,缩短操作数据库的时间)
package com.jeeplus.modules.sms.utils.jdk8;
import com.jeeplus.modules.sms.entity.vo.TestDataVo;
import org.apache.commons.lang3.time.DateUtils;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class ThreadUtils {
public static void main(String[] args) throws Exception {
// 创建一个固定大小的线程池
ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
Date now = new Date();
Date nextDate = DateUtils.parseDate("2020", "YYYY");
//CompletableFuture组合式异步编程
List<CompletableFuture<Void>> futures = IntStream.range(0, 5).mapToObj(j->CompletableFuture.runAsync(()->{
List<TestDataVo> list = IntStream.range(0, 20)
.mapToObj(i ->new TestDataVo().setName(""+i))
.collect(Collectors.toList());
//数据库操作逻辑
// ...
// ...
}, service)
).collect(Collectors.toList());
//开始等待所有任务执行完成,线程join是可以加入等待有顺序的,如果其他方法用到了,那当前的先执行
CompletableFuture.allOf(futures.toArray(new CompletableFuture[]{})).join();
}
}
4 for循环操作集合等,能用并行流parallelStream()尽量用并行流,时间能节省2/3左右,线程不安全可以通过synchronize,加重入锁ReentrantLock ,collect和reduce收集集合数据让操作变得安全
package com.jeeplus.modules.sms.utils.jdk8;
import org.junit.Test;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* Stream操作的三个步骤
*
* 创建stream
* 中间操作(过滤、map)
* 终止操作
* 总结就是paralleStream里直接去修改变量是非线程安全的,但是采用collect和reduce操作就是满足线程安全的了。
* 解决办法
* 加同步带吗快sycynize
* 加重入锁ReentrantLock
* 用collect和reduce收集集合数据
*/
public class Stream {
private static List<Integer> list1 = new ArrayList<>();
private static List<Integer> list2 = new ArrayList<>();
private static List<Integer> list3 = new ArrayList<>();
private static Lock lock = new ReentrantLock();
@Test
public void main22() {
List<Integer> numberList = Arrays.asList(1,2,3,4,5,6,7,8,9);
Set<Integer> collect = numberList.parallelStream().map(Integer::intValue).collect(Collectors.toSet());
System.out.println(collect.toArray());
List<Integer> collect1 = numberList.parallelStream().map(Integer::intValue).collect(Collectors.toList());
System.out.println(collect1.toArray());
BinaryOperator<Integer> accumulator=new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return 2;
}
};
Optional<Integer> reduce = numberList.parallelStream().map(Integer::intValue).reduce(accumulator);
System.out.println(reduce.get());
}
/**
* 线程安全的
*/
@Test
public void main11() {
Collection<Object> collection = new HashSet<Object>();
collection.add("af");
collection.add("afss");
Collection<Object> synchronizedCollection = Collections.synchronizedCollection(collection);
//这种遍历方式因为是异步遍历(线程不安全所以要包装collection),会产生一种情况,就是遍历的顺序是无序的,
// 当然也有相应的好处就是,遍历速度会快,当对生成结果不考虑排序问题而且数据量比较大的时候可以使用.
if (!CollectionUtils.isEmpty(collection)) {
collection.parallelStream().forEach(role -> {
synchronizedCollection.add(role.toString());
synchronizedCollection.forEach(System.out::println);
});
}
}
@Test
public void main0() {
IntStream.range(0, 10000).forEach(list1::add);
IntStream.range(0, 10000).parallel().forEach(list2::add);
IntStream.range(0, 10000).parallel().forEach(i -> {
lock.lock();
try {
list3.add(i);
}finally {
lock.unlock();
}
});
System.out.println("串行执行的大小:" + list1.size());
System.out.println("并行执行的大小:" + list2.size());
System.out.println("加锁并行执行的大小:" + list3.size());
}
public static void main(String[] args) {
// create();
// 创建出一个数组
List<String> strList = Arrays.asList("YangHang", "AnXiaoHei", "LiuPengFei");
//strList.forEach(System.out::println);
strList.stream().forEach(System.out::println);
strList.parallelStream().forEach(System.out::println);
boolean anXiaoHei = strList.stream().anyMatch((a) -> a.equals("AnXiaoHei"));
System.out.println(anXiaoHei);
Optional<String> first = strList.stream().findFirst();
System.out.println(first.get());
boolean anXiaoHei1 = strList.stream().anyMatch((a) -> a.equals("AnXiaoHei"));
System.out.println(anXiaoHei1);
long count = strList.stream().count();
System.out.println(count);
}
/**
* 运行结果:
* For execute time cost 9003 ms
* Stream execute time cost 9054 ms
* ParallelStream execute time cost 2007 ms
* ParallelStream forEachOrdered execute time cost 9013 ms
* parallelStream().forEach是通过多线程并行的方式来执行我们的代码,而parallelStream(). forEachOrdered也是采用多线程,
* 但由于加入了顺序执行约束,故程序是采用多线程同步的方式运行的,最终耗时与for、stream两种单线程执行的耗时接近
*/
@Test
public void main3() throws InterruptedException {
System.out.println("运行结果:");
List<Integer> numberList = Arrays.asList(1,2,3,4,5,6,7,8,9);
//for
Long forBegin = System.currentTimeMillis();
for(Integer number : numberList){
//System.out.println(String.format("For The Current Thread's ID is %d and output number %d ",Thread.currentThread().getId(),number));
Thread.sleep(1000);
}
System.out.println(String.format("For execute time cost %d ms",System.currentTimeMillis()-forBegin));
System.out.println("\r");
// stream method
Long streamBegin = System.currentTimeMillis();
numberList.stream().forEach(number -> {
//System.out.println(String.format("Stream The Current Thread's ID is %d and output number %d ",Thread.currentThread().getId(),number));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(String.format("Stream execute time cost %d ms",System.currentTimeMillis()-streamBegin));
System.out.println("\r");
// parallelStream method
Long parallelStreamBegin = System.currentTimeMillis();
numberList.parallelStream().forEach(number -> {
//System.out.println(String.format("ParallelStream The Current Thread's ID is %d and output number %d ",Thread.currentThread().getId(),number));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(String.format("ParallelStream execute time cost %d ms",System.currentTimeMillis()-parallelStreamBegin));
System.out.println("\r");
// parallelStream method
Long parallelStreamForEachOrderBegin = System.currentTimeMillis();
numberList.parallelStream().forEachOrdered(number -> {
//System.out.println(String.format("ParallelStream forEachOrdered The Current Thread's ID is %d and output number %d ",Thread.currentThread().getId(),number));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(String.format("ParallelStream forEachOrdered execute time cost %d ms",System.currentTimeMillis()-parallelStreamForEachOrderBegin));
System.out.println("\r");
}
/**
* 运行结果:
* Stream The Current Thread's ID is 1 and output number 1
* Stream The Current Thread's ID is 1 and output number 2
* Stream The Current Thread's ID is 1 and output number 3
* Stream The Current Thread's ID is 1 and output number 4
* Stream The Current Thread's ID is 1 and output number 5
* Stream The Current Thread's ID is 1 and output number 6
* Stream The Current Thread's ID is 1 and output number 7
* Stream The Current Thread's ID is 1 and output number 8
* Stream The Current Thread's ID is 1 and output number 9
*
* ParallelStream The Current Thread's ID is 1 and output number 6
* ParallelStream The Current Thread's ID is 1 and output number 5
* ParallelStream The Current Thread's ID is 12 and output number 3
* ParallelStream The Current Thread's ID is 1 and output number 2
* ParallelStream The Current Thread's ID is 15 and output number 7
* ParallelStream The Current Thread's ID is 13 and output number 8
* ParallelStream The Current Thread's ID is 1 and output number 1
* ParallelStream The Current Thread's ID is 12 and output number 4
* ParallelStream The Current Thread's ID is 15 and output number 9
*
* ParallelStream forEach Ordered The Current Thread's ID is 16 and output number 1
* ParallelStream forEach Ordered The Current Thread's ID is 14 and output number 2
* ParallelStream forEach Ordered The Current Thread's ID is 14 and output number 3
* ParallelStream forEach Ordered The Current Thread's ID is 14 and output number 4
* ParallelStream forEach Ordered The Current Thread's ID is 14 and output number 5
* ParallelStream forEach Ordered The Current Thread's ID is 14 and output number 6
* ParallelStream forEach Ordered The Current Thread's ID is 14 and output number 7
* ParallelStream forEach Ordered The Current Thread's ID is 14 and output number 8
* ParallelStream forEach Ordered The Current Thread's ID is 14 and output number 9
*/
@Test
public void main2() {
System.out.println("运行结果:");
List<Integer> numberList = Arrays.asList(1,2,3,4,5,6,7,8,9);
// stream method
numberList.stream().forEach(number -> {
System.out.println(String.format("Stream The Current Thread's ID is %d and output number %d ",Thread.currentThread().getId(),number));
});
System.out.println("\r");
// parallelStream method
numberList.parallelStream().forEach(number -> {
System.out.println(String.format("ParallelStream The Current Thread's ID is %d and output number %d ",Thread.currentThread().getId(),number));
});
System.out.println("\r");
// parallelStream method
numberList.parallelStream().forEachOrdered(number -> {
System.out.println(String.format("ParallelStream forEach Ordered The Current Thread's ID is %d and output number %d ",Thread.currentThread().getId(),number));
});
System.out.println("\r");
}
/**
*
运行结果:
1 2 3 4 5 6 7 8 9
6 5 3 8 4 2 9 7 1
1 2 3 4 5 6 7 8 9
*/
@Test
public void main() {
List<Integer> numberList = Arrays.asList(1,2,3,4,5,6,7,8,9);
System.out.println("运行结果:");
// stream method
numberList.stream().forEach(number -> {
System.out.print(String.format("%d ",number));
});
System.out.println("\r");
// parallelStream method
numberList.parallelStream().forEach(number -> {
System.out.print(String.format("%d ",number));
});
System.out.println("\r");
// parallelStream method
numberList.parallelStream().forEachOrdered(number -> {
System.out.print(String.format("%d ",number));
});
System.out.println("\r");
}
/**
* stream的创建:
*/
private static void create() {
// 1,校验通过Collection 系列集合提供的stream()或者paralleStream()
List<String> list = new ArrayList<>();
java.util.stream.Stream<String> stream = list.stream();
// 2.通过Arrays的静态方法stream()获取数组流
String[] str = new String[10];
java.util.stream.Stream<String> stream2 = Arrays.stream(str);
}
}
5 CompletableFuture 多线程安全问题处理建议
package com.jeeplus.modules.sms.utils.jdk8;
import com.google.common.collect.Lists;
import com.jeeplus.modules.sms.entity.vo.TestDataVo;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;
import java.util.stream.IntStream;
/**
*
CompletableFuture API
runAsync方法不支持返回值。
supplyAsync可以支持返回值。
whenComplete 和 whenCompleteAsync 的区别:
whenComplete:是执行当前任务的线程执行继续执行 whenComplete 的任务。
whenCompleteAsync:是执行把 whenCompleteAsync 这个任务继续提交给线程池来进行执行。
thenApply 方法当一个线程依赖另一个线程时,可以使用 thenApply 方法来把这两个线程串行化。
handle 是执行任务完成时对结果的处理。
handle 方法和 thenApply 方法处理方式基本一样。不同的是 handle 是在任务完成后再执行,还可以处理异常的任务。thenApply 只可以执行正常的任务,任务出现异常则不执行 thenApply 方法。
thenAccept 消费处理结果接收任务的处理结果,并消费处理,无返回结果。跟 thenAccept 方法不一样的是,不关心任务的处理结果。只要上面的任务执行完成,就开始执行 thenAccept 。
thenCombine 会把 两个 CompletionStage 的任务都执行完成后,把两个任务的结果一块交给 thenCombine 来处理。
thenAcceptBoth当两个CompletionStage都执行完成后,把结果一块交给thenAcceptBoth来进行消
applyToEither 方法两个CompletionStage,谁执行返回的结果快,我就用那个CompletionStage的结果进行下一步的转化操作。
acceptEither 方法两个CompletionStage,谁执行返回的结果快,我就用那个CompletionStage的结果进行下一步的消耗操作。
runAfterEither 方法两个CompletionStage,任何一个完成了都会执行下一步的操作(Runnable)
runAfterBoth两个CompletionStage,都完成了计算才会执行下一步的操作(Runnable)
thenCompose 方法thenCompose 方法允许你对两个 CompletionStage 进行流水线操作,第一个操作完成时,将其结果作为参数传递给第二个操作。
CountDownLatch 包里面的CountDownLatch其实可以把它看作一个计数器,只不过这个计数器的操作是原子操作,同时只能有一个线程去操作这个计数器,也就是同时只能有一个线程去减这个计数器里面的值
你可以向CountDownLatch对象设置一个初始的数字作为计数值,任何调用这个对象上的await()方法都会阻塞,直到这个计数器的计数值被其他的线程减为0为止。
CompletableFuture的allOf方法来实现多实例的同时返回
总结一下:CompletableFuture多线程任务执行线程不安全解决方案,保持数据原子性
1 操作的数据用collect/reduce收集
2 使用CopyOnWriteArrayList(合适读多写少的情况)替换ArrayList
3 Collections.synchronizedList设置为线程安全类(这里测试比CopyOnWriteArrayList快)
4 使用join方法再添加到ArrayList
5 CountDownLatch原子计数器(和4类似)
*/
public class CompletableFutureTest {
private Logger logger = LoggerFactory.getLogger(getClass());
// 创建一个固定大小的线程池
ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
@Test
public void test() {
//异步调用阿里短信接口发送短信
CompletableFuture.runAsync(new Runnable() {
@Override
public void run() {
try {
System.out.println("发送短信成功1:{}\",\"send sms-------------\"");
} catch (Exception e) {
System.out.println("发送短信失败1:{}\",\"send sms-------------\"");
}
}
},service);
CompletableFuture.runAsync(() -> {
try {
System.out.println("发送短信成功2:{}\",\"send sms-------------\"");
} catch (Exception e) {
System.out.println("发送短信失败2:{}\",\"send sms-------------\"");
}
});
}
private static final int SIZE = 1000000;
public static void main(String[] args) {
testCompletableFuture();
testCompletableFuture2();
testCompletableFuture3();
}
/**
* 非线程安全,有时出现数组下标越界问题
*/
public static void testCompletableFuture() {
System.out.println("----start----" + LocalDateTime.now());
//由于方法后面的add操作,这里的变量相当于全局变量,造成了线程安全问题出现
List<Integer> list = new ArrayList<>();
List<CompletableFuture<Void>> futureList = new ArrayList<>();
IntStream.range(0, SIZE).forEach(i -> {
//设置随机返回数字
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
Random random = new Random();
return random.nextInt(Integer.MAX_VALUE);
}).thenAccept(list::add); //添加到list,出现线程安全问题
futureList.add(future);
});
//主线程阻塞等待所有异步线程完成任务
futureList.forEach(CompletableFuture::join);
System.out.println("testCompletableFuture - size :" + list.size());
System.out.println("----end----" + LocalDateTime.now());
System.out.println();
}
/**
* 线程安全
* 方法一:使用CopyOnWriteArrayList(合适读多写少的情况)替换ArrayList
* 方法二:Collections.synchronizedList设置为线程安全类(这里测试比CopyOnWriteArrayList快)
*/
public static void testCompletableFuture2() {
System.out.println("----start----" + LocalDateTime.now());
List<Integer> list = Collections.synchronizedList(new ArrayList<>());
// List<Integer> list = new CopyOnWriteArrayList<>();
List<CompletableFuture<Void>> futureList = new ArrayList<>();
IntStream.range(0, SIZE).forEach(i -> {
//设置随机返回数字
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
Random random = new Random();
return random.nextInt(Integer.MAX_VALUE);
}).thenAccept(list::add); //添加到list,不会出现线程安全问题
futureList.add(future);
});
//主线程阻塞等待所有异步线程完成任务
futureList.forEach(CompletableFuture::join);
System.out.println("testCompletableFuture2 - size : " + list.size());
System.out.println("----end----" + LocalDateTime.now());
System.out.println();
}
/**
* 线程安全
* 使用join方法再添加到ArrayList
*/
public static void testCompletableFuture3() {
System.out.println("----start----" + LocalDateTime.now());
List<Integer> list = new ArrayList<>();
List<CompletableFuture<Integer>> futureList = new ArrayList<>();
IntStream.range(0, SIZE).forEach(i -> {
//设置随机返回数字
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
Random random = new Random();
return random.nextInt(Integer.MAX_VALUE);
});
futureList.add(future);
});
//主线程阻塞等待所有异步线程完成任务,并在join返回结果再添加到ArrayList,就不会出现线程安全问题
futureList.forEach(future-> list.add(future.join()));
System.out.println("testCompletableFuture3 - size : " + list.size());
System.out.println("----end----" + LocalDateTime.now());
}
@Test
public void hello () throws InterruptedException, ExecutionException {
List<TestDataVo> list = Lists.<TestDataVo>newArrayList();
CountDownLatch countDownLatch = new CountDownLatch(2);
CompletableFuture<TestDataVo> userFuture1 = CompletableFuture.supplyAsync(() -> {
try {
return new TestDataVo().setName("ss");
} catch (Exception e) {
logger.error("落库失败:{}", e.getMessage());
} finally {
countDownLatch.countDown();
}
return null;
},service);
CompletableFuture<TestDataVo> userFuture2 = CompletableFuture.supplyAsync(() -> {
try {
return new TestDataVo().setName("asffas");
} catch (Exception e) {
logger.error("落库失败:{}", e.getMessage());
} finally {
countDownLatch.countDown();
}
return null;
},service);
countDownLatch.await();
list.add(userFuture1.get());
list.add(userFuture2.get());
countDownLatch.await();
list.parallelStream().forEach(System.out::println);
}
}