Fork me on GitHub

RecursiveTask和RecursiveAction的使用 以及java 8 并行流和顺序流

原文地址: https://blog.csdn.net/weixin_41404773/article/details/80733324

什么是Fork/Join框架

Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。

我们再通过Fork和Join这两个单词来理解下Fork/Join框架,Fork就是把一个大任务切分为若干子任务并行的执行,Join就是合并这些子任务的执行结果,最后得到这个大任务的结果。比如计算1+2+。。+10000,可以分割成10个子任务,每个子任务分别对1000个数进行求和,最终汇总这10个子任务的结果。Fork/Join的运行流程图如下:

工作窃取算法

工作窃取(work-stealing)算法是指某个线程从其他队列里窃取任务来执行。工作窃取的运行流程图如下:

那么为什么需要使用工作窃取算法呢?假如我们需要做一个比较大的任务,我们可以把这个任务分割为若干互不依赖的子任务,为了减少线程间的竞争,于是把这些子任务分别放到不同的队列里,并为每个队列创建一个单独的线程来执行队列里的任务,线程和队列一一对应,比如A线程负责处理A队列里的任务。但是有的线程会先把自己队列里的任务干完,而其他线程对应的队列里还有任务等待处理。干完活的线程与其等着,不如去帮其他线程干活,于是它就去其他线程的队列里窃取一个任务来执行。而在这时它们会访问同一个队列,所以为了减少窃取任务线程和被窃取任务线程之间的竞争,通常会使用双端队列,被窃取任务线程永远从双端队列的头部拿任务执行,而窃取任务的线程永远从双端队列的尾部拿任务执行。

工作窃取算法的优点是充分利用线程进行并行计算,并减少了线程间的竞争,其缺点是在某些情况下还是存在竞争,比如双端队列里只有一个任务时。并且消耗了更多的系统资源,比如创建多个线程和多个双端队列。

ForkJoinPool

Java提供了ForkJoinPool来支持将一个任务拆分成多个“小任务”并行计算,再把多个“小任务”的结果合成总的计算结果。

ForkJoinPool是ExecutorService的实现类,因此是一种特殊的线程池。ForkJoinPool提供了如下两个常用的构造器。

  • public ForkJoinPool(int parallelism):创建一个包含parallelism个并行线程的ForkJoinPool

  • public ForkJoinPool() :以Runtime.getRuntime().availableProcessors()的返回值作为parallelism来创建ForkJoinPool

创建ForkJoinPool实例后,可以调用ForkJoinPool的submit(ForkJoinTask task)或者invoke(ForkJoinTask task)来执行指定任务。其中ForkJoinTask代表一个可以并行、合并的任务。ForkJoinTask是一个抽象类,它有两个抽象子类:RecursiveAction和RecursiveTask。

  • RecursiveTask代表有返回值的任务
  • RecursiveAction代表没有返回值的任务。

RecursiveAction

下面以一个没有返回值的大任务为例,介绍一下RecursiveAction的用法。

大任务是:打印0-100的数值。

小任务是:每次只能打印20个数值。

代码执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.example.jedis.test;

import java.util.concurrent.RecursiveAction;

/**
*
* @Author : Wukn
* @Date : 2018/2/5
*/
public class RaskDemo extends RecursiveAction {
/**
* 每个"小任务"最多只打印20个数
*/
private static final int MAX = 20;

private int start;
private int end;

public RaskDemo(int start, int end) {
this.start = start;
this.end = end;
}

@Override
protected void compute() {
//当end-start的值小于MAX时,开始打印
if((end-start) < MAX) {
for(int i= start; i<end;i++) {
System.out.println(Thread.currentThread().getName()+"i的值"+i);
}
}else {
// 将大任务分解成两个小任务
int middle = (start + end) / 2;
RaskDemo left = new RaskDemo(start, middle);
RaskDemo right = new RaskDemo(middle, end);
left.fork();
right.fork();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) throws Exception{
// 创建包含Runtime.getRuntime().availableProcessors()返回值作为个数的并行线程的ForkJoinPool
ForkJoinPool forkJoinPool = new ForkJoinPool();

// 提交可分解的PrintTask任务
forkJoinPool.submit(new RaskDemo(0, 1000));

//阻塞当前线程直到 ForkJoinPool 中所有的任务都执行结束
forkJoinPool.awaitTermination(2, TimeUnit.SECONDS);

// 关闭线程池
forkJoinPool.shutdown();
}

运行结果

从上面结果来看,ForkJoinPool启动了四个线程来执行这个打印任务,我的计算机的CPU是四核的。大家还可以看到程序虽然打印了0-999这一千个数字,但是并不是连续打印的,这是因为程序将这个打印任务进行了分解,分解后的任务会并行执行,所以不会按顺序打印。

RecursiveTask

下面以一个有返回值的大任务为例,介绍一下RecursiveTask的用法。

阅读更多...

分布式缓存Redis之与Memcached的比较

文章地址 Redis和Memcache的区别总结-京东阿里面试

数据类型

Redis支持的数据类型要丰富得多,Redis不仅仅支持简单的k/v类型的数据,同时还提供String,List,Set,Hash,Sorted Set,pub/sub,Transactions数据结构的存储。其中Set是HashMap实现的,value永远为null而已

memcache支持简单数据类型,需要客户端自己处理复杂对象

持久性

redis支持数据落地持久化存储,

可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。

memcache不支持数据持久存储

分布式存储

redis支持master-slave复制模式

memcache可以使用一致性hash做分布式

value大小不同

memcache是一个内存缓存,key的长度小于250字符,单个item存储要小于1M,不适合虚拟机使用

数据一致性不同

Redis只使用单核,而Memcached可以使用多核,所以平均每一个核上Redis在存储小数据时比Memcached性能更高。而在100k以上的数据中,Memcached性能要高于Redis,虽然Redis最近也在存储大数据的性能上进行优化,但是比起Memcached,还是稍有逊色。

redis使用的是单线程模型,保证了数据按顺序提交。

memcache需要使用cas保证数据一致性。CAS(Check and Set)是一个确保并发一致性的机制,属于“乐观锁”范畴;原理很简单:拿版本号,操作,对比版本号,如果一致就操作,不一致就放弃任何操作

cpu利用

redis单线程模型只能使用一个cpu,可以开启多个redis进程

HashMap为什么要用红黑树?

文章地址 数据结构笔试面试知识集合之HashMap的红黑树

当HashMap中有大量的元素存放都同一个桶中时,这个桶下有一条长长的链表,这个时候HashMap就相当于一个单链表,假如单链表有n个元素,遍历的时间复杂度就是O(n),完全失去了它的优势。

针对这种情况,JDK1.8中引入了红黑树来优化这个问题,为什么不引入二叉查找树呢?因为二叉查找树的一般操作的执行时间为O(lgn),但是二叉查找树若退化成了一棵具有n个结点的线性链后,则这些操作最坏情况运行时间为O(n)。与单链表一样。

所以此时我们需要红黑树它在二叉查找树的基础上增加了着色和相关的性质使得红黑树相对平衡,从而保证了红黑树的查找、插入、删除的时间复杂度最坏为O(log n)。

tcp三次握手、四次挥手

视频讲解总结1

视频地址 tcp三次握手、四次挥手

  1. SYN J
  2. SYN K, ACK J+1 服务器对 J+1说明服务器准备好了
  3. ACK K+1 客户端对 K+1 说明客户端准备好了

在TCP中,如果有一方收到了对方的数据,一定会发送ACK确认包给发送方

而在UDP中,没有这个过程,因此导致了TCP稳定,而UDP不稳定

  1. FIN seq = x+2 ACK = y +1 FIN(final)客户端要close的标志,ACK是确认收到了上次数据包
  2. ACK = x + 3 收到了
  3. FIN seq = y + 1 服务端调用socket close 发送FIN告诉客户端我也要关了
  4. ACK = y + 2 客户端表示收到了

TCP为什么是四次,而不是三次?

收到主动方发来的FIN报,被动方会立刻回答主动方,让主动方进入FIN-WAIT-1状态。防止主动方重复的发FIN报。

阅读更多...

使用 Google jib 快速构建springboot项目镜像

google jib 是Google于18年7月发布的一个针对Java应用的构建镜像的工具(支持Maven和Gradle) ,好处是能够复用构建缓存,能够加快构建,减小传输体积(后文会详细讲解),并且让Java工程师不需要理解Docker相关知识就可以简单构建镜像并且发布到指定registry里(不需要docker build , tag, push)

项目地址: https://github.com/hacker-and-painter/springboot-jib-docker

运行效果

1
2
3
docker pull gaohanghang/springboot-jib-docker

docker run -p 8080:8080 -t gaohanghang/springboot-jib-docker

快速开始

  1. dockerhub中创建Repository

  1. 创建springboot项目,添加HelloController.java
1
2
3
4
5
6
7
8
@RestController
public class HelloController {

@GetMapping("/")
public String hello() {
return "hello";
}
}
  1. pom.xml中添加jib plugin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>1.3.0</version>
<configuration>
<from>
<!--base image-->
<image>openjdk:alpine</image>
</from>
<to>
<!--<image>registry.cn-hangzhou.aliyuncs.com/m65536/jibtest</image>-->
<!--目标镜像registry地址,为了方便测试,你需要换成自己的地址,如果你的网络不好,可以选用国内加速器,比如阿里云的-->
<image>registry.hub.docker.com/gaohanghang/springboot-jib-docker</image>
</to>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
  1. <image>registry.hub.docker.com/gaohanghang/springboot-jib-docker</image>中的gaohanghang/springboot-jib-docker修改为你在dockerhub中创建的Repository

  2. 使用maven package打包项目,jib会自动构建镜像到dockerhub

  1. 拉取镜像并运行
1
2
3
docker pull gaohanghang/springboot-jib-docker

docker run -p 8080:8080 -t gaohanghang/springboot-jib-docker

参考文章

Jib构建你的第一个java镜像
谷歌开源 Java 镜像构建工具 Jib
加速和简化构建Docker(基于Google jib)

使用vue2.0实现购物车和地址选配功能

第1章 课程介绍

1-1 Vue基础知识介绍

1. Vue基础知识介绍

易用

灵活 - 渐进式

高效

  • 16kb min+gzip 的运行大小

  • 超快虚拟 DOM

  • 最省心的优化

2. Vue基础指令介绍

  • 指令的使用: v-model v-text v-show v-if v-bind v-for
  • 过滤器filter(对接口获得的数据做业务转换)
  • 组件Component

第2章 创建Vue实例

2-1 创建Vue实例

《微习惯》读书笔记

豆瓣地址 https://book.douban.com/subject/26877306/

1天一个俯卧撑-1天一页书-1天50字 | 每天只做一点点的无负担习惯养成法

一生二,二生三,三生万物

第1章 微习惯是什么

微习惯就是你强迫自己每天做的微不足道的积极行为

一个行为变成习惯所需的时间平均为66天

第2章 大脑的工作原理

大脑是我的一切,华生,身体只是附件而已。

—— 阿瑟 · 柯南 · 道儿

大脑分为主动与被动部分(即意识和潜意识部分)

前额皮层控制意识部分,基底神经节控制潜意识部分

第3章 动力 v.s. 意志力

激发动力策略的诸多问题

  • 动力并不可靠
  • 你不会每次都激发动力
  • 有人习惯性的认为动力是行动的唯一基础
  • “热情递减法则” 是动力让我们失败的原因

为什么意志力能打败动力

  • 意志力可靠
  • 意志力可以被强化
  • 意志力策略可以通过计划执行

意志力的工作原理

  1. 做决定会消耗意志力

  2. 意志力元分析

    自我损耗的5个最重要因素

    • 努力程度
    • 感知难度
    • 消极情绪
    • 主观疲劳
    • 血糖水平

小结

  • 我们使用动力或意志力开启新的(非习惯)行为的
  • 动力不可靠,所以不能充当建立习惯的策略
  • 意志力可靠,但前提是你没有把它耗尽
  • 引起一直损耗的5个主要因素:努力程度、感知程度、消息情绪、主观疲劳和血糖水平
  • 如果我们能成功克服这个5项障碍,我们就应该能走向成功

第4章 微习惯策略

塑造你生活的不是你偶尔做的一两件事,而是你一贯坚持做的事。

—— 安东尼 · 罗宾

以微习惯方式运用意志力

努力程度

微习惯只需要非常少的努力

感知难度

不会感到困难

消极情绪

微习惯不会感觉到任何消极情绪

主观疲劳

主观疲劳无法消除,但是微习惯可以有效缓解主观疲劳

血糖水平

虽然血糖水平和微习惯相互独立,但微习惯的确会节约能力和意志力,而且会在心理上不断给予你能量

微习惯如何拓宽你的舒适区

一小步 + 想做的事 = 较高的进一步的可能性

出现阻力的两个时间节点

行动前的阻力

继续行动时面对的阻力

跨域障碍的微习惯

精神障碍: 你有锻炼的经历,却不想锻炼

身体障碍: 因为很累,所以你的身体并不想锻炼

如果连渺小的习惯目标都无法实现,这多让人无地自容

把微习惯融入生活

第5章 微习惯的独特之处

是故胜兵先胜而后求战,败兵先战而后求胜

微习惯能与现有习惯一较高下

大脑会抗拒大幅度的改变

微习惯就像消耗很低的特洛伊木马

微步骤 + 意志力是必胜组合

微习惯没有截止时间

微习惯能提升自我效能感

微习惯策略是一个产出自我效能感的机器

微习惯能给予你自主权

完成微习惯后,你就可以自由选择做自己想做的事情

抽象和具体目标与微习惯相结合

微习惯能促进抽象目标和具体目标的进展

远离恐惧、怀疑、胆怯或犹豫

行动是征服这些消极情绪的最佳武器

微习惯增强正念和意志力,给你超乎想象的惊喜

第6章 彻底改变只需八步

一个得不到执行的念头只会消亡

—— 罗杰 · 冯 · 欧克

第1步: 选择适合你的微习惯和计划

一周弹性计划(推荐)

单一微计划

多项微计划

把你的微计划变成”小的不可思议的一小步”

如何培养每周微习惯

第2步: 挖掘每个微习惯的内在价值

用”为什么”找到源头

第3步: 明确习惯依据,将其纳入日程

培养习惯的依据有两种: 时间和行为方式

自由度高德非具体习惯

把上床睡觉作为一天结束的标志

决策时刻

带有一项依据的微习惯成功率会更高

第4步: 建立回报机制,以奖励提升成就感

回报关联

大笑会释放出让你心情变好的化学物质,可以看些搞笑视频来

微习惯建立在享受和巧用微小成就感的基础上

回报策略

成功会带来更多成功

回报对意志力测重建作用

第5步: 记录与追踪完成情况

大日历(推荐)

数字化追踪

  • Lift
  • Habit Streak Pian
  • Joes Goals
  • Goals On Track

第6步: 微量开始,超额完成

强化意志力的微习惯

带来进步的微习惯

A. 微习惯超额环节

B. 微习惯安全网(培养实实在在的微习惯)

减轻意志力损耗的微习惯

第7步: 服从计划安排,拜托高期待值

第8步: 微习惯养成的标志

  • 没有抵触情绪
  • 身份
  • 行动无需考虑
  • 你不再担心了
  • 常态化
  • 它很无聊

对超额完成任务的态度

超额完成了任务,你很了不起

没有超额完成任务,你还是很了不起

第7章 微习惯策略的八大规则

1. 绝不要自欺欺人

不要调高期待值

2. 满意每一个进步

微习惯是一种行动优于动力的生活哲理

要满意,但别满足

3. 经常回报自己,尤其在完成微习惯之后

回报也能给你回报

4. 保持头脑清醒

要力求维持冷静的思维模式并信任你选择的策略

5. 感到强烈抵触时,后退一步并缩小目标

6. 提醒自己这件事很轻松

7. 绝不要小看微步骤

8. 用多余精力超额完成任务,而不是制定更大目标

联合索引的最左前缀匹配原则

作者:Jokerone_

链接:https://www.jianshu.com/p/b7911e0394b0

1
2
3
4
5
6
7
8
CREATE TABLE `user2` (
`userid` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) NOT NULL DEFAULT '',
`password` varchar(20) NOT NULL DEFAULT '',
`usertype` varchar(20) NOT NULL DEFAULT '',
PRIMARY KEY (`userid`),
KEY `a_b_c_index` (`username`,`password`,`usertype`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

上表中有一个联合索引,下面开始验证最左匹配原则。
当存在username时会使用索引查询:

上表中有一个联合索引,下面开始验证最左匹配原则。
当存在username时会使用索引查询:

1
explain select * from user2 where username = '1' and password = '1';

当没有username时,不会使用索引查询:

1
explain select * from user2 where password = '1';

当有username,但顺序乱序时也可以使用索引:

1
explain select * from user2 where password = '1' and username = '1';

在最左匹配原则中,有如下说明:

  1. 最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。

  2. =和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式

Java 访问权限控制:你真的了解 protected 关键字吗?

版权声明:

本文原创作者:书呆子Rico
作者博客地址:http://blog.csdn.net/justloveyou_/

摘要:

  对于类的成员而言,其能否被其他类所访问,取决于该成员的修饰词;而对于一个类而言,其能否被其他类所访问,也取决于该类的修饰词。在Java中,类成员访问权限修饰词有四类:private,无(包访问权限),protected 和 public,而其中只有包访问权限和public才能修饰一个类(内部类除外)。特别地,很多Java书籍对protected可见性的介绍都比较笼统,本文重点说明了protected关键字的可见性内涵,并介绍了一些其他的修饰符。

一. Package

 关于包的使用,只需注意一点:在一个项目中,不可以有相同的两个包名,也就是说,包名不能和项目中其他的包名重复,这里不但包括自定义包名也包括项目所引用的类库的包名。看下面例子:

1
2
3
4
5
6
7
8
package java.lang;

public class MyObject {
public static void main(String[] args) throws CloneNotSupportedException {
Object o = new Object();
System.out.println(o.hashCode());
}
}

 我们给自己的程序设置的包名是java.lang,事实上,java.lang是JDK使用的包名。这时,程序可以正常编译,但当我们运行程序时会有包冲突警告并抛出 “java.lang.SecurityException: Prohibited package name: java.lang”,如下图所示:

 此外,我们需要注意:在程序中,package语句必须是文件中除注释外第一句程序代码,否则不能通过编译。

二. Java访问权限概述

   对于一个类,其成员(包括成员变量和成员方法)能否被其他类所访问,取决于该成员的修饰词。在Java中,类成员的访问权限修饰词有四个:private,无(包访问权限),protected 和 public,其权限控制如下表所示:

  特别要注意的是,不同于类成员的访问权限类别,对于非内部类而言,类的访问权限修饰词仅有public和包访问权限两种(内部类可以是private或protected的,关于内部类进一步了解请见我的博客《Java 内部类综述》)。特别地,如果你不希望其他任何人对该类拥有访问权,你可以把所有的构造器都指定为private的,从而阻止任何人创建该类的对象。这个时候,该类的对象就只能在其static成员内部进行创建,这种情形有点像单例模式,例如像下面的例子那样:

1
2
3
4
5
6
7
8
class Test {
// private Constructor!
private Test() {}
// Allow creation via static method:
public static Test getTest() {
return new Test();
}
}
阅读更多...

用了 Lambda 之后,发现可以忘记设计模式了

原文地址: https://mp.weixin.qq.com/s/Xj4wjzfVkFJz8HWKahjmhA

设计模式是过去的一些好的经验和套路的总结,但是好的语言特性可以让开发者不去考虑这些设计模式。面向对象常见的设计模式有策略模式、模板方法、观察者模式、责任链模式以及工厂模式,使用Lambda表达式(函数式编程思维)有助于避免面向对象开发中的那些固定代码。下面挑选了策略模式和职责链模式两个案例进行分析。

案例1:策略模式

当我们解决一个问题有不同的解法的时候,又不希望客户感知到这些解法的细节,这种情况下适合使用策略模式。策略模式包括三个部分:

  • 解决问题的算法(上图中的Strategy);
  • 一个或多个该类算法的具体实现(上图中的ConcreteStrategyA、ConcreteStrategyB和ConcreteStrategyC)
  • 一个或多个客户使用场景(上图中的ClientContext)

面向对象思路

首先定义策略接口,表示字符串的处理算法:

然后定义具体的实现类,即不同的验证算法:

最后定义客户使用场景,代码如下图所示。Validator是为客户提供服务时使用的上下文环境,每个Valiator对象中都封装了具体的Strategy对象,在实际工作中,我们可以通过更换具体的Strategy对象来进行客户服务的升级,而且不需要让客户进行升级。

函数式编程思路

如果使用Lambda表达式考虑,你会发现ValidationStrategy就是一个函数接口(还与Predicate具有同样的函数描述),那么就不需要定义上面那些实现类了,可以直接用下面的代码替换,原因是Lambda表达式内部已经对这些类进行了一定的封装。

案例2:责任链模式

在某些场景下,需要对一个对象做一系列的工作,这些工作分别是由不同的类完成的,这时候就比较适合使用责任链模式。责任链模式的主要组成部分包括三个:

  • 管理操作序列的抽象类,在该抽象类里有会有一个对象记录当前对象的后继操作对象;
  • 一些具体的操作对象,这些操作对象会以一个链表的形式组织起来
  • 一个使用该模式的客户端组件,该组件只需要跟一个组件打交道就好,不需要跟很多个操作对象耦合在一起。

面向对象思路

首先看下我们这里定义了一个抽象类ProcessingObject,其中successor字段用于管理该对象的后继操作对象;handle接口作为对外提供服务的接口;handleWork作为实际处理对象的操作方法。

阅读更多...
  • Copyrights © 2015-2023 高行行
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信