【小家java】java5新特性(简述十大新特性) 重要一跃
【小家java】java6新特性(简述十大新特性) 鸡肋升级
【小家java】java7新特性(简述八大新特性) 不温不火
【小家java】java8新特性(简述十大新特性) 饱受赞誉
【小家java】java9新特性(简述十大新特性) 褒贬不一
【小家java】java10新特性(简述十大新特性) 小步迭代
【小家java】java11新特性(简述八大新特性) 首个重磅LTS版本
迷茫,就是不想做小事,只想做大事。不想做眼前的事,只想做天边的事。不想做繁琐的事,只想做繁荣的事。其实没有迷茫,只有对自己的高估。低下头,脚踏实地了,这病也就好了
说这个命题之前,我先解释一下编程里,有状态和无状态都什么意思
下面我们先看看Srping中的Bean来辅助理解:
Servlet、Out,Request,Response,Session,Config,Page,PageContext是线程安全的,Application在整个系统内被使用,所以不是线程安全的.
比如map或者filter会从输入流中获取每一个元素,并且在输出流中得到一个结果,这些操作没有内部状态,称为无状态操作。
但是像reduce、sum、max这些操作都需要内部状态来累计计算结果,所以称为有状态操作。
这里需要单独解释一下:
有一些操作sort、distinct、limit、skip看上去和filter、map差不多,他们接收一个流,再生成一个流,但是区别在于排序和去重复项需要知道先前的历史。比如排序就需要将所有元素放入缓存区后才能给输出流加入一个项目,这个操作对缓存的要求是无上限的,流有多大就需要多大的缓存才能进行运算。这些操作也是有状态操作。
所以判断流操作是否有状态的判断标准,就是看是否需要知道先前的数据历史。前后数据是否有依赖关系来判断
中间操作就像是水管的一部分,终端操作就像水龙头,增加水管长度不会消耗水,只有打开水龙头才会开始消耗水。
peek方法一般在debug的时候才会开启
下面举个例子,体验一把有状态和无状态:
String str = "my name is fangshixiang";
Stream.of(str.split(" ")).peek(x -> System.out.println(Thread.currentThread().getName() + "___" + x))
.map(x -> x.length()).count();
输出:
main___my
main___name
main___is
main___fangshixiang
我们发现,打印的线程都是main主线程,因为我们是串行流嘛。
现在我们试试并行流打印输出:
public static void main(String[] args) {
//打印每个单词的长度
String str = "my name is fangshixiang";
Stream.of(str.split(" ")).parallel().peek(x -> System.out.println(Thread.currentThread().getName() + "___" + x))
.map(x -> x.length()).count();
}
输出:
ForkJoinPool.commonPool-worker-1___fangshixiang
main___is
ForkJoinPool.commonPool-worker-2___name
ForkJoinPool.commonPool-worker-3___my
看出效果了没?并行流输出无顺序。并行流采用的ForkJoin框架的线程池ForkJoinPool.commonPool
。所有的并行流都会使用同一个线程池,所以如果并行流太多的话,也会出现阻塞的。因此若需要,我们可以让它使用我们自己的线程池,来提高效率。
备注ForkJoinPool.commonPool的线程池大小默认为CPU核心数量。当然可以通脱System.setProperty手动修改这个值,只是一般都没有需要
自定义线程池例子:
public static void main(String[] args) {
//定义自己的线程池
ForkJoinPool pool = new ForkJoinPool(10);
String str = "my name is fangshixiang";
pool.execute(() -> Stream.of(str.split(" ")).parallel().peek(x -> System.out.println(Thread.currentThread().getName() + "___" + x))
.map(x -> x.length()).count());
pool.shutdown(); //关闭线程池(一般都不需要关的)
//下面代码是为了让main线程不要退出,因为如果退出太早,上面不会有东西输出的
//提示一点 wait外面必须有synchronized关键字,因为没有会报错的
synchronized (pool) {
try {
pool.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
输出:
ForkJoinPool-1-worker-2___name
ForkJoinPool-1-worker-13___my
ForkJoinPool-1-worker-9___is
ForkJoinPool-1-worker-11___fangshixiang
可以明显看出,这里输出的就是我们自己自定义的线程池了,就能很好的保证效率了。
无状态演示:(必须那并行流演示) 因为串行流将没有任何效果,因为是线程安全的
public static void main(String[] args) {
//打印每个单词的长度
String str = "my name is fangshixiang good";
Stream.of(str.split(" "))
.parallel()
.peek(x -> System.out.println(Thread.currentThread().getName() + "___" + x))
.map(x -> x.length())
//.sorted()
.peek(x -> System.out.println(Thread.currentThread().getName() + "___" + x))
.count();
}
输出:
ForkJoinPool.commonPool-worker-1___name
ForkJoinPool.commonPool-worker-3___good
ForkJoinPool.commonPool-worker-1___4
ForkJoinPool.commonPool-worker-1___fangshixiang
ForkJoinPool.commonPool-worker-1___12
ForkJoinPool.commonPool-worker-2___my
ForkJoinPool.commonPool-worker-2___2
main___is
main___2
ForkJoinPool.commonPool-worker-3___4
虽然每次执行的顺序不一样,但是每次main都是处理is和2。再看下面加入sorted
第一次执行:
main___good
ForkJoinPool.commonPool-worker-1___name
ForkJoinPool.commonPool-worker-1___my
main___fangshixiang
ForkJoinPool.commonPool-worker-1___is
main___4
ForkJoinPool.commonPool-worker-3___2
ForkJoinPool.commonPool-worker-2___4
ForkJoinPool.commonPool-worker-1___12
ForkJoinPool.commonPool-worker-3___2
再次执行:
main___good
ForkJoinPool.commonPool-worker-2___is
main___fangshixiang
ForkJoinPool.commonPool-worker-1___name
ForkJoinPool.commonPool-worker-3___my
main___4
ForkJoinPool.commonPool-worker-1___12
main___4
ForkJoinPool.commonPool-worker-2___2
ForkJoinPool.commonPool-worker-3___2
我们会发现每次的结果都不一样,原因就是因为sorted是有状态的,所以有了很多的不确定性。
若群二维码失效,请加微信号(或者扫描下方二维码):fsx641385712。
并且备注:“java入群” 字样,会手动邀请入群