【译】你可能不知道但却很有用的 Java 特性

标签: 翻译 Java | 发表时间:2022-01-17 02:28 | 作者:
出处:http://crossoverjie.top/

原文链接

在这篇文章中你将会学习到一些你可能没听过但有用的 Java 特性,这些是我个人常用的一些特性或者是从其他文章中学习到的,重点是关注 API 而不是语言本身。

延迟队列

众所周知,在 Java 中有许多类型的集合可以使用,但你听说过 DelayQueue 吗?它是一个特定类型的集合,允许我们基于延时时间对数据排序,这是一个非常有意思的类,它实现了 BlockingQueue 接口,只有当数据过期后才能从队列里取出。

使用它的第一步,你的 class 需要实现 Delayed 接口中的 getDelay 方法,当然也可以不用声明一个 class,使用 Record 也是可以的。

这是 Java14 的新特性

     
1
2
3
4
5
6
7
8
9
10
11
12
     
public record DelayedEvent(long startTime, String msg) implements Delayed {
public long getDelay(TimeUnit unit) {
long diff = startTime - System.currentTimeMillis();
return unit.convert(diff, TimeUnit.MILLISECONDS);
}
public int compareTo(Delayed o) {
return (int) (this.startTime - ((DelayedEvent) o).startTime);
}
}

假设我们需要一个延时 10s 取出的数据,我们只需要放入一个比当前时间多 10s 的任务即可。

     
1
2
3
4
5
     
final DelayQueue<DelayedEvent> delayQueue = new DelayQueue<>();
final long timeFirst = System.currentTimeMillis() + 10000;
delayQueue.offer(new DelayedEvent(timeFirst, "1"));
log.info("Done");
log.info(delayQueue.take().msg());

最终输出如下:

时间格式的日期

这个特性可能对大部分人来说没什么用,但老实说我个人非常喜欢;不管怎么说 Java 8 在时间 API 上改进了许多。从这个版本开始或许你不再需要其他任何扩展库了。

你能想到嘛,从 Java 16 中你甚至可以用标准库表示一天内的日期了,比如 “in the morning” “in the afternoon” ,这是一个新的格式语句 B

     
1
2
3
4
     
String s = DateTimeFormatter
.ofPattern("B")
.format(LocalDateTime.now());
System.out.println(s);

以下是我的输出,具体和你当前时间有关。

你可能会想为什么会是调用 “B” 呢,这确实看起来不太直观,通过下表也许能解答疑惑:

Stamped Lock

在我看来,并发包是 Java 中最有意思的包之一,同时又很少被开发者熟练掌握,特别是长期使用 web 开发框架的开发者。

有多少人曾经使用过 Lock 呢?相对于 synchronized 来说这是一种更灵活的线程同步机制。

从 Java8 开始你可以使用一种新的锁: StampedLock.StampedLock,能够替代 ReadWriteLock

假设现在有两个线程,一个线程更新金额、一个线程读取余额;更新余额的线程首先需要读取金额,再多线程的情况下需要某种同步机制(不然更新数据会发生错误),第二个线程用乐观锁的方式读取余额。

     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
     
StampedLock lock = new StampedLock();
Balance b = new Balance(10000);
Runnable w = () -> {
long stamp = lock.writeLock();
b.setAmount(b.getAmount() + 1000);
System.out.println("Write: " + b.getAmount());
lock.unlockWrite(stamp);
};
Runnable r = () -> {
long stamp = lock.tryOptimisticRead();
if (!lock.validate(stamp)) {
stamp = lock.readLock();
try {
System.out.println("Read: " + b.getAmount());
} finally {
lock.unlockRead(stamp);
}
} else {
System.out.println("Optimistic read fails");
}
};

现在更新和读取的都用 50 个线程来进行测试,最终的余额将会等于 60000.

     
1
2
3
4
5
     
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 50; i++) {
executor.submit(w);
executor.submit(r);
}

并发累加器

锁并并不是并发包中唯一有意思的特性,并发累加器也同样有趣;它可以根据我们提供的函数更新数据;再多线程更新数据的场景下, LongAccumulator 是比 AtomicLong 更优的选择。

现在让我们来看看具体如何使用,我们需要两个参数进行初始化;第一个是用于累加计算的函数,通常是一个 sum 函数,第二个参数则是累加计算的初始化值。

接下来我们用 10000 作为初始值来创建一个 LongAccumulator,最终结果是多少?其实结果与上文相同,都是 60000,但这次我们并没有使用锁。

     
1
2
3
4
5
6
7
8
9
10
11
12
     
LongAccumulator balance = new LongAccumulator(Long::sum, 10000L);
Runnable w = () -> balance.accumulate(1000L);
ExecutorService executor = Executors.newFixedThreadPool(50);
for (int i = 0; i < 50; i++) {
executor.submit(w);
}
executor.shutdown();
if (executor.awaitTermination(1000L, TimeUnit.MILLISECONDS))
System.out.println("Balance: " + balance.get());
assert balance.get() == 60000L;

数组的二分查找

假设我们想在一个排序列表中插入一个新元素,可以使用 Arrays.binarySearch() 函数,当这个 key 存在时将会返回 key 所在的索引,如果不存在时将会返回插入的位置 -(insertion point)-1

binarySearch 是 Java 中非常简单且有效的查询方法。

下面的这个例子中,对返回结果取反便能的到索引位置。

     
1
2
3
4
     
int[] t = new int[] {1, 2, 4, 5};
int x = Arrays.binarySearch(t, 3);
assert ~x == 2;

负数的二进制是以正数的补码表示,对一个数取反+1 就等于补码,所以这里直接取反就等于 Arrays.binarySearch() 不存在时的返回值了。

Bit Set

如果你需要对二进制数组进行操作你会怎么做?用 boolean[] 布尔数组?

有一种更高效又更省内存的方式,那就是 BitSet。它允许我们存储和操作 bit 数组,与 boolean[] 相比可省 8 倍的内存;也可以使用 and/or/xor 等逻辑操作。

假设我们现在有两个 bit 数组,我们需要对他们进行 xor 运算;我们需要创建两个 BitSet 实例,然后调用 xor 函数。

     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
     
BitSet bs1 = new BitSet();
bs1.set(0);
bs1.set(2);
bs1.set(4);
System.out.println("bs1 : " + bs1);
BitSet bs2 = new BitSet();
bs2.set(1);
bs2.set(2);
bs2.set(3);
System.out.println("bs2 : " + bs2);
bs2.xor(bs1);
System.out.println("xor: " + bs2);

最终的输出结果如下:

相关 [知道 java] 推荐:

Java你不知道的那些事儿—Java隐藏特性(上)

- - Java - 编程语言 - ITeye博客
每种语言都很强大,不管你是像我一样的初学者还是有过N年项目经验的大神,总会有你不知道的东西. 就其语言本身而言,比如Java,也许你用Java开发 了好几年,对其可以说是烂熟于心,但你能保证Java所有的用法你都知道吗. 今天没事就来整理下Java中有哪些隐藏的特性呢. 一、双括号初始化语法( DoubleBraceInitialization)(这里指的是大括号{}).

Java 8全面解析!不知道的来看看那!

- - CSDN博客互联网推荐文章
java8的面世惊动了不少业界人员,让我们一起来看看吧. 函数式接口是只定义了一个抽象方法的接口. Java 8引入了 FunctionalInterface注解来表明一个接口打算成为一个函数式接口. 例如,java.lang.Runnable就是一个函数式接口. 注意,不管FunctionalInterface注解是否存在,Java编译器都会将所有满足该定义的接口看作是函数式接口......

你应当知道的7个Java工具

- - ImportNew
Alex Zhitnitsky告诉我们这7个辅助工具的主要功能特点,这些工具每个java程序员都应该了解一下. 这篇文章最初发表在takipi的博客–Java与Scala异常分析和性能监控.. 在准备进行锁和负载测试之前,应该对一些最新的最具创新性的工具有一个快速了解. 为了防止你错过这些信息,rebellabs最近公布了对Java工具和技术全景的一个全球性调查 结果.

java日志,需要知道的几件事

- - Java - 编程语言 - ITeye博客
java日志,需要知道的几件事. 如果对于commons-loging 、log4j 、slf4j 、LogBack 等都已经非常清楚了,可以忽略本文. 几次解决日志冲突问题时对这几个概念的简单总结,希望对这块基础没有理解透的同学能有所帮助,当然如果对这块有更深刻理解的同学,也贡献出自己的知识和见解.

Java程序员应该知道的10个调试技巧

- - 博客 - 伯乐在线
摘要:调试不仅可以查找到应用程序缺陷所在,还可以解决缺陷. 对于Java 程序员来说,他们不仅要学会如何在Eclipse里面开发像样的程序,更需要学会如何调试程序. 本文介绍了Java程序员必知的10个调试技巧,保证让你受益匪浅. 调试可以帮助识别和解决应用程序缺陷,在本文中,作者将使用大家常用的的开发工具Eclipse来调试Java应用程序.

打造高性能JAVA应用你需要知道的

- - ImportNew
这篇文章节选自《java performance》,对java性能比较关心的同学大概都知道这本书,性能这个东西可能是很多同学在日常写java code的时候很少去关心的,但是在我们写code的过程中确又时时离不开对程序性能的影响,小到我们使用位运算来实现算术运算,大到我们对JAVA代码的总体架构设计,性能其实离我们很近.

【译】你可能不知道但却很有用的 Java 特性

- - crossoverJie's Blog
在这篇文章中你将会学习到一些你可能没听过但有用的 Java 特性,这些是我个人常用的一些特性或者是从其他文章中学习到的,重点是关注 API 而不是语言本身. 众所周知,在 Java 中有许多类型的集合可以使用,但你听说过 DelayQueue 吗. 它是一个特定类型的集合,允许我们基于延时时间对数据排序,这是一个非常有意思的类,它实现了 BlockingQueue 接口,只有当数据过期后才能从队列里取出.

(收藏)Java程序员也应该知道的too many open files的一些事

- - jackyrong
Java应用也有很大的概率会碰到too many open files这样的异常,此异常的主要原因是进程打开了太多的文件(可通过lsof | grep [pid] | wc -l来查看),linux对每个用户/进程都会有打开的文件数的限制. 当前进程打开的文件数的限制可通过cat /proc/[pid]/limits来查看,看到的内容类似如下:.

[iteye]每个Java开发者都应该知道的5个JDK工具

- - PHP & Java
【编者按】JDK是Java语言的软件开发工具包,没有它就无法编译Java程序. 目前,有许许多多的JDK工具呈现在大家面前,但最常用的莫过于java.exe、javac.exe、jar等. 本文作者Joe拥有多年的Java开发经验,其在博客上分享了一篇文章: 5 JDK Tools Every Java Developer Should Know,笔者对其进行了编译,以下为译文.

Java中的锁(Locks in Java)

- - 并发编程网 - ifeve.com
原文链接 作者:Jakob Jenkov 译者:申章 校对:丁一. 锁像synchronized同步块一样,是一种线程同步机制,但比Java中的synchronized同步块更复杂. 因为锁(以及其它更高级的线程同步机制)是由synchronized同步块的方式实现的,所以我们还不能完全摆脱synchronized关键字( 译者注:这说的是Java 5之前的情况).