(Guava 译文系列)图
Guava 的common.graph
库对图结构进行了建模,图,是一种包含实体及其之间关系的数据结构。这种结构的例子包括
web
页面与超链接、科学家和他们写的论文、机场以及机场之间的航路、以及个人与其家庭关系(谱系树)。图结构的目的在于能提供一种通用且可扩展的语言来描述上述这类数据。
Guava 的common.graph
库对图结构进行了建模,图,是一种包含实体及其之间关系的数据结构。这种结构的例子包括
web
页面与超链接、科学家和他们写的论文、机场以及机场之间的航路、以及个人与其家庭关系(谱系树)。图结构的目的在于能提供一种通用且可扩展的语言来描述上述这类数据。
以某种分隔符连接连接字符串序列通常不应该那么麻烦,当序列中包含 null
值的时候尤甚。流式风格的 Joiner
简化了这种操作。
1 | Joiner joiner = Joiner.on("; ").skipNulls(); |
上述代码将返回字符串:”Harry; Ron;
Hermione“。除了skipNulls
以外,还可以用useForNull(String)
来指定将
null 替换为给定的字符串。
你也可以直接对对象使用 Joiner
,这会自动调用对象的toString()
方法后进行拼接。
1 | Joiner.on(",").join(Arrays.asList(1, 5, 7)); // returns "1,5,7" |
注意:joiner 实例是不可变的。因此 joiner
的配置方法总会返回一个新的
Joiner
,并通过这种方式来获取所需语义的
joiner。这种特性让Joiner
能够线程安全,并可以将之定义为一个static final
的常量。
在我的文章 虚拟工厂:Java 线程池 中简单介绍了采用
”工厂“ 这样一个现实生活中的概念来抽象具体的线程操作,并定义了
Worker
TaskQueue
等概念。用这种方式拉近了计算机域和真实世界域之间的距离,让代码表现现实的意图。
不仅仅是线程池,从 Java 8
开始引入的Stream
也一样,它用代码构建了一套流水线体系,通过流水线环节的叠加来实现对流水线上元素的各种处理。
现实生活中,存在大大小小的工厂,他们提供特定的服务,根据用户的需求生产、加工出产品,交付给客户,并赚取服务费。
通常,我们只需要交给工厂一份图纸,工厂就会根据图纸来生产产品,而生产的过程我们是无需关心的,作为客户,我们只关心这间工厂什么时候把产品交付给我们。
类似的,在代码里我们也会存在以下场景:主流程代码将需要做的事情分成一件件任务,并交由对应的代码工厂去执行,这里的任务可以是代码逻辑的执行步骤,而最基本的代码工厂就是各种方法。合并起来,代码工厂执行任务,实际上就是调用方法执行业务逻辑。
不过有时我们期望代码工厂能够异步执行任务,这样主流程就不用一直等待它执行完成了:让互相之间不存在依赖的任务并行执行能大大缩短总体的执行时间。
Java 提供了线程机制允许我们将方法调用放在与主流程独立的线程中执行,不过直接使用线程代码写起来略为繁琐,线程本身也是较低层的概念,所以在 JDK 1.5 之后,提供了对异步任务执行的高层抽象:线程池,通过线程池我们可以更方便的执行异步任务,而不必关心线程的使用细节。
线程池以清晰的设计与简洁的代码优雅的封装了线程并实现了任务执行逻辑。阅读它的代码,就好像走进了一间虚拟的工厂,工人挥舞工具的画面似乎就在眼前。
在本站早期的文章Build your own blog site by using GithubPages + Hexo + Travis CI中描述了如何使用 Hexo 和 Travis 来在 Git Pages 服务上搭建自己的个人博客。
但使用了一段时间之后,发现 Git Pages 的方案存在一些问题: - 访问速度慢。不走 VPN 访问 Github 速度感人,Git Pages 也一样,各种资源、图片加载缓慢。 - 难以被 Baidu 收录。网友提供了各种办法来解决,但都挺麻烦。
所以当我了解到 Gitee 也提供了免费的静态网页服务后,决定试试看,但毕竟一边在 github 上更新,一边在 gitee 更新,既麻烦又容易出错,所以期望能够直接把 github 上的更新自动同步到 gitee 上。
经过一些实验后,我成功的实现了把 Git Pages 镜像到 Gitee Pages 的需求。
在前一篇有关 Redis 分布式锁的文章中,我们讨论了几点有关分布式锁的要求: 1. 操作原子性 2. 可重入性 3. 效率
为了满足上述条件,采用 本地锁 + Redis 锁
的方式解决了问题。不过在文章末尾提到,Redis
不保证强一致性,因此对一致性要求很高的场景会存在安全隐患。
本文将讨论使用满足 CP 要求的 ZooKeeper 来实现强一致性的分布式锁。
rust 的 module system,类似 java 的 package,可以用于将代码分别放置在适合他们的单元内。同时 rust 还允许用户控制 module 之间的可见性(public/private)。
在 module 内,rust 允许用户放置 function,struct,trait,struct implement 以及 child module。
rust 对 module 的要求是任何 module 都应处在一颗以 root module 为根的 module tree 中。期望访问某个 module 时,能够找到一条通路从root一路向下直到该 module。
不说什么具体的知识,我们先一步步的,来写个最简单的测试,并且让他啊跑起来,看看
rust 下的测试是什么样子的: 1. 创建个 lib
工程:cargo new simplest-test --lib
2. 在
src/lib.rs
里面,rust 已经自动帮我们写下了如下代码:
1
2
3
4
5
6
7#[cfg(test)]
mod test {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}cargo test
1
2
3
4
5
6
7
8
9
10
11
12
13
14 Compiling simplest-test v0.1.0 (/User/xxx/simplest-test)
Finished dev [unoptimized + debuginfo] target(s) in 5.91s
Running target/debug/deps/simplest_test-c430fbaec5f55b85
running 1 test
test tests::it_works ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Doc-tests simplest-test
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
经过上述三步,我们已经创建了一个最简单的测试工程,并且运行了自带的测试。
在代码层面,测试本身无需多说,结构上我们看到,与业务代码不同,测试在
module 上增加了 attribute: #[cfg(test)]
,在测试方法上增加了
attribute: #[test]
。 -
#[cfg(test)]
:配置编译条件,只有在 test
模式下,被标记的代码块才会被编译(换句话说,它确保 release
中不包含测试代码) -
#[test]
:被标记的方法将被视为测试来执行
在 output 中,还包含了两部分“running x test”,第一部分是我们已有的测试,第二部分为文档测试,本文暂不涉及。
在如今这样一个张口分布式,闭口微服务的软件开发趋势下,多实例似乎已经不是某种选择而是一个无需多说的基本技术要求了。
多实例为我们带来稳定性提升的同时,也伴随着更复杂的技术要求,原先在本地即可处理的问题,全部扩展为分布式问题,其中就包含我们今天会聊到的多实例同步即分布式锁问题。JDK 提供的锁实现已经能够非常好的解决本地同步问题,而扩展到多实例环境下,Redis、ZooKeeper 等优秀的实现也使得我们使用分布式锁变得更加简单。
其实对于分布式锁的原理、分布式锁的 Redis 实现、ZK 实现等等各类文章不计其数,然而只要简单一搜就会发现,大多数文章都在教大家 Redis 分布式锁的原理和实现方法,但却没有几篇会写什么实现是好的实现,是适合用于生产环境,高效而考虑全面的实现。这将是本文讨论的内容。