LocalDate、LocalTime、LocalDateTime类的实例是不可变的对象,分别表示使用 ISO-8601 日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息。
// LocalDate:获取日期时间的信息。格式为 2019-10-16
@Test
public void test01() {
// 创建指定日期
LocalDate fj = LocalDate.of(1985, 9, 23);
System.out.println("fj = " + fj); // 1985-09-23
// 得到当前日期
LocalDate nowDate = LocalDate.now();
System.out.println("nowDate = " + nowDate); // 2019-10-16
// 获取日期信息
System.out.println("年: " + nowDate.getYear());
System.out.println("月: " + nowDate.getMonthValue());
System.out.println("日: " + nowDate.getDayOfMonth());
System.out.println("星期: " + nowDate.getDayOfWeek());
}
// LocalTime类: 获取时间信息。格式为 16:38:54.158549300
@Test
public void test02() {
// 得到指定的时间
LocalTime time = LocalTime.of(12,15, 28, 129_900_000);
System.out.println("time = " + time);
// 得到当前时间
LocalTime nowTime = LocalTime.now();
System.out.println("nowTime = " + nowTime);
// 获取时间信息
System.out.println("小时: " + nowTime.getHour());
System.out.println("分钟: " + nowTime.getMinute());
System.out.println("秒: " + nowTime.getSecond());
System.out.println("纳秒: " + nowTime.getNano());
}
// LocalDateTime类: 获取日期时间信息。格式为 2018-09-06T15:33:56.750
@Test
public void test03() {
LocalDateTime fj = LocalDateTime.of(1985, 9, 23, 9, 10, 20);
System.out.println("fj = " + fj); // 1985-09-23T09:10:20
// 得到当前日期时间
LocalDateTime now = LocalDateTime.now();
System.out.println("now = " + now); // 2019-10-16T16:42:24.497896800
System.out.println(now.getYear());
System.out.println(now.getMonthValue());
System.out.println(now.getDayOfMonth());
System.out.println(now.getHour());
System.out.println(now.getMinute());
System.out.println(now.getSecond());
System.out.println(now.getNano());
}
// LocalDateTime类: 对日期时间的修改
@Test
public void test05() {
LocalDateTime now = LocalDateTime.now();
System.out.println("now = " + now);
// 修改日期时间
LocalDateTime setYear = now.withYear(2078);
System.out.println("修改年份: " + setYear);
System.out.println("now == setYear: " + (now == setYear));
System.out.println("修改月份: " + now.withMonth(6));
System.out.println("修改小时: " + now.withHour(9));
System.out.println("修改分钟: " + now.withMinute(11));
// 再当前对象的基础上加上或减去指定的时间
LocalDateTime localDateTime = now.plusDays(5);
System.out.println("5天后: " + localDateTime);
System.out.println("now == localDateTime: " + (now == localDateTime));
System.out.println("10年后: " + now.plusYears(10));
System.out.println("20月后: " + now.plusMonths(20));
System.out.println("20年前: " + now.minusYears(20));
System.out.println("5月前: " + now.minusMonths(5));
System.out.println("100天前: " + now.minusDays(100));
}
@Test
public void test06() {
// 在JDK8中,LocalDate类中使用isBefore()、isAfter()、equals()方法来比较两个日期,可直接进行比较。
LocalDate now = LocalDate.now();
LocalDate date = LocalDate.of(2018, 8, 8);
System.out.println(now.isBefore(date)); // false
System.out.println(now.isAfter(date)); // true
}
通过 java.time.format.DateTimeFormatter 类可以进行日期时间解析与格式化。
@Test
public void test04() {
// 得到当前日期时间
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 将日期时间格式化为字符串
String format = now.format(formatter);
System.out.println("format = " + format);
// 将字符串解析为日期时间
LocalDateTime parse = LocalDateTime.parse("1985-09-23 10:12:22", formatter);
System.out.println("parse = " + parse);
}
// 时间戳
@Test
public void test07() {
Instant now = Instant.now();
System.out.println("当前时间戳 = " + now);
// 获取从1970年1月1日 00:00:00的秒
System.out.println(now.getNano());
System.out.println(now.getEpochSecond());
System.out.println(now.toEpochMilli());
System.out.println(System.currentTimeMillis());
Instant instant = Instant.ofEpochSecond(5);
System.out.println(instant);
}
Duration/Period类: 计算日期时间差。
// Duration/Period类: 计算日期时间差
@Test
public void test08() {
// Duration计算时间的距离
LocalTime now = LocalTime.now();
LocalTime time = LocalTime.of(14, 15, 20);
Duration duration = Duration.between(time, now);
System.out.println("相差的天数:" + duration.toDays());
System.out.println("相差的小时数:" + duration.toHours());
System.out.println("相差的分钟数:" + duration.toMinutes());
System.out.println("相差的秒数:" + duration.toSeconds());
// Period计算日期的距离
LocalDate nowDate = LocalDate.now();
LocalDate date = LocalDate.of(1998, 8, 8);
// 让后面的时间减去前面的时间
Period period = Period.between(date, nowDate);
System.out.println("相差的年:" + period.getYears());
System.out.println("相差的月:" + period.getMonths());
System.out.println("相差的天:" + period.getDays());
}
// TemporalAdjuster类:自定义调整时间
@Test
public void test09() {
LocalDateTime now = LocalDateTime.now();
// 得到下一个月的第一天
TemporalAdjuster firsWeekDayOfNextMonth = temporal -> {
LocalDateTime dateTime = (LocalDateTime) temporal;
LocalDateTime nextMonth = dateTime.plusMonths(1).withDayOfMonth(1);
System.out.println("nextMonth = " + nextMonth);
return nextMonth;
};
LocalDateTime nextMonth = now.with(firsWeekDayOfNextMonth);
System.out.println("nextMonth = " + nextMonth);
}
// 设置日期时间的时区
@Test
public void test10() {
// 1.获取所有的时区ID
// ZoneId.getAvailableZoneIds().forEach(System.out::println);
// 不带时间,获取计算机的当前时间
LocalDateTime now = LocalDateTime.now(); // 中国使用的东八区的时区.比标准时间早8个小时
System.out.println("now = " + now);
// 2.操作带时区的类
// now(Clock.systemUTC()): 创建世界标准时间
ZonedDateTime bz = ZonedDateTime.now(Clock.systemUTC());
System.out.println("bz = " + bz);
// now(): 使用计算机的默认的时区,创建日期时间
ZonedDateTime now1 = ZonedDateTime.now();
System.out.println("now1 = " + now1); // 2019-10-19T16:19:44.007153500+08:00[Asia/Shanghai]
// 使用指定的时区创建日期时间
ZonedDateTime now2 = ZonedDateTime.now(ZoneId.of("America/Vancouver"));
System.out.println("now2 = " + now2); // 2019-10-19T01:21:44.248794200-07:00[America/Vancouver]
}
自从Java 5中引入 注解 以来,注解开始变得非常流行,并在各个框架和项目中被广泛使用。不过注解有一个很大的限制是:在同一个地方不能多次使用同一个注解。JDK 8引入了重复注解的概念,允许在同一个地方多次使用同一个注解。在JDK 8中使用@Repeatable注解定义重复注解。
@Retention(RetentionPolicy.RUNTIME)
@interface MyTests {
MyTest[] value();
}
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(MyTests.class)
@interface MyTest {
String value();
}
@MyTest("tbc")
@MyTest("tba")
@MyTest("tba")
public class Demo01 {
@MyTest("mbc")
@MyTest("mba")
public void test() throws NoSuchMethodException {
}
}
@MyTest("tbc")
@MyTest("tba")
@MyTest("tba")
public class Demo01 {
@Test
@MyTest("mbc")
@MyTest("mba")
public void test() throws NoSuchMethodException {
// 4.解析得到类上的指定注解
MyTest[] tests = Demo01.class.getAnnotationsByType(MyTest.class);
for (MyTest test : tests) {
System.out.println(test.value());
}
// 得到方法上的指定注解
Annotation[] tests1 = Demo01.class.getMethod("test").getAnnotationsByType(MyTest.class);
for (Annotation annotation : tests1) {
System.out.println("annotation = " + annotation);
}
}
}
类型注解的使用
JDK 8为@Target元注解新增了两种类型: TYPE_PARAMETER , TYPE_USE 。 TYPE_PARAMETER :表示该注解能写在类型参数的声明语句中。 类型参数声明如:
TYPE_PARAMETER的使用
@Target(ElementType.TYPE_PARAMETER)
@interface TyptParam {
}
public class Demo02<@TyptParam T> {
public static void main( String[] args) {
}
public <@TyptParam E> void test( String a) {
}
}
TYPE_USE的使用
// 3.配置多个重复的注解
@Target(ElementType.TYPE_USE)
@interface NotNull {
}
public class Demo02<@TyptParam T extends String> {
private @NotNull int a = 10;
public static void main(@NotNull String[] args) {
@NotNull int x = 1;
@NotNull String s = new @NotNull String();
}
public <@TyptParam E> void test( String a) {
}
}
小结
曾经有个项目希望实现一个这样的技术需求:从方法中获取形参的真实名称,然后转成某个xml
的一个属性。当时本以为很简单,可以直接通过API获取,但是通过反射获取的形参的名称总是:arg0
这样的参数。项目在当时使用的JDK7,所以强行给每个形参加上注解
指明方法的形参,类似:
public void execute(@param("taskId") String taskId) {
System.out.println(taskId);
}
相信大家也看到了,这个做法确实不够优雅,但是在当时应该是唯一的解决办法了。
假设有如下方法:
class Executor {
public void execute(String taskId) {
System.out.println(taskId);
}
}
我需要从execute
方法中获取参数taskId
这个形参的名称:taskId
在Java8中需要进行环境变量配置才可以通过如下代码获取:
public static void main(String[] args) throws NoSuchMethodException {
Class executorClass = Executor.class;
Method method = executorClass.getMethod("execute", String.class);
for (Parameter parameter : method.getParameters()) {
String name = parameter.getName();
System.out.println(name);
}
}
配置
Map 新增
public static void main(String[] arg) { Map map = new HashMap(); //如果key不存在返回默认值 System.out.println(map.getOrDefault("a", 123)); map.put("a", 123); //将lambda结果放入该key map.compute("a", (key, value) -> key + "" + value); System.out.println(map.get("a"));//a123 //如果key不存在,则将lambda结果放入该key map.computeIfAbsent("a", (key) -> key + "234"); //如果key存在,则将lambda结果更新该key,如果lambda结果返回null,则移除该key map.computeIfPresent("a", (key, value) -> key + "kv" + value);//->akva123 //如果key对应的值为null则用新value更新,否则用lambda结果更新,如果lambda的值为null,则移除此key //lambda表达式中参数oldvalue是原key对应的value,newvalue是第二个参数指定的值 map.merge("a", "newvalue", (oldvalue, newvalue) -> oldvalue + "dv" + newvalue);//->akva123dvnewvalue //如果key不存在,则将值放入此key map.putIfAbsent("b", "b123"); //当key存在并且key对应的value等于新的值时,移除此key map.remove("b", "b124"); //如果存在key,则替换key的值 map.replace("b", "b125"); //遍历 map.forEach((key, value) -> System.out.println("key:" + key + ";value:" + value));
处理数值
Java8添加了对无符号数的额外支持。Java中的数值总是有符号的,例如,让我们来观察Integer:int可表示最多2 ** 32个数。Java中的数值默认为有符号的,所以最后一个二进制数字表示符号(0为正数,1为负数)。所以从十进制的0开始,最大的有符号正整数为2 ** 31 - 1。
你可以通过Integer.MAX_VALUE来访问它:
System.out.println(Integer.MAX_VALUE); // 2147483647
System.out.println(Integer.MAX_VALUE + 1); // -2147483648
long maxUnsignedInt = (1l << 32) - 1;
String string = String.valueOf(maxUnsignedInt);
int unsignedInt = Integer.parseUnsignedInt(string, 10);
String string2 = Integer.toUnsignedString(unsignedInt, 10);
就像你看到的那样,现在可以将最大的无符号数2 ** 32 - 1解析为整数。而且你也可以将这个数值转换回无符号数的字符串表示。这在之前不可能使用parseInt完成,就像这个例子展示的那样:
try {
Integer.parseInt(string, 10);
}
catch (NumberFormatException e) {
System.err.println("could not parse signed int of " + maxUnsignedInt);
}
这个数值不可解析为有符号整数,因为它超出了最大范围2 ** 31 - 1。 算术运算Math工具类新增了一些方法来处理数值溢出。这是什么意思呢? 我们已经看到了所有数值类型都有最大值。所以当算术运算的结果不能被它的大小装下时,会发生什么呢?
System.out.println(Integer.MAX_VALUE); // 2147483647
System.out.println(Integer.MAX_VALUE + 1); // -2147483648
就像你看到的那样,发生了整数溢出,这通常是我们不愿意看到的。Java8添加了严格数学运算的支持来解决这个问题。Math扩展了一些方法,它们全部以exact结尾,例如addExact。当运算结果不能被数值类型装下时,这些方法通过抛出ArithmeticException异常来合理地处理溢出。
try {
Math.addExact(Integer.MAX_VALUE, 1);
}
catch (ArithmeticException e) {
System.err.println(e.getMessage());
// => integer overflow
}
当尝试通过toIntExact将长整数转换为整数时,可能会抛出同样的异常:
try {
Math.toIntExact(Long.MAX_VALUE);
}
catch (ArithmeticException e) {
System.err.println(e.getMessage());
// => integer overflow
}
处理文件
Files工具类首次在Java7中引入,作为NIO的一部分。JDK8 API添加了一些额外的方法,它们可以将文件用于函数式数据流。让我们深入探索一些代码示例。列出文件Files.list方法将指定目录的所有路径转换为数据流,便于我们在文件系统的内容上使用类似filter和sorted的流操作。
try (Stream stream = Files.list(Paths.get(""))) {
String joined = stream
.map(String::valueOf)
.filter(path -> !path.startsWith("."))
.sorted()
.collect(Collectors.joining("; "));
System.out.println("List: " + joined);
}
下面的例子演示了如何查找在目录及其子目录下的文件:
Path start = Paths.get("");
int maxDepth = 5;
try (Stream stream = Files.find(start, maxDepth, (path, attr) ->
String.valueOf(path).endsWith(".js"))) {
String joined = stream
.sorted()
.map(String::valueOf)
.collect(Collectors.joining("; "));
System.out.println("Found: " + joined);
}
Path start = Paths.get("");
int maxDepth = 5;
try (Stream stream = Files.walk(start, maxDepth)) {
String joined = stream
.map(String::valueOf)
.filter(path -> path.endsWith(".js"))
.sorted()
.collect(Collectors.joining("; "));
System.out.println("walk(): " + joined);
}
将文本文件读到内存,以及向文本文件写入字符串在Java 8 中是简单的任务。不需要再去摆弄读写器了。Files.readAllLines从指定的文件把所有行读进字符串列表中。你可以简单地修改这个列表,并且将它通过Files.write写到另一个文件中:
List lines = Files.readAllLines(Paths.get("res/nashorn1.js"));
lines.add("print('foobar');");
Files.write(Paths.get("res/nashorn1-modified.js"), lines);
要注意这些方法对内存并不十分高效,因为整个文件都会读进内存。文件越大,所用的堆区也就越大。你可以使用Files.lines方法来作为内存高效的替代。这个方法读取每一行,并使用函数式数据流来对其流式处理,而不是一次性把所有行都读进内存。
try (Stream stream = Files.lines(Paths.get("res/nashorn1.js"))) {
stream
.filter(line -> line.contains("print"))
.map(String::trim)
.forEach(System.out::println);
}
Path path = Paths.get("res/nashorn1.js");
try (BufferedReader reader = Files.newBufferedReader(path)) {
System.out.println(reader.readLine());
}
Path path = Paths.get("res/output.js");
try (BufferedWriter writer = Files.newBufferedWriter(path)) {
writer.write("print('Hello World');");
}
Path path = Paths.get("res/nashorn1.js");
try (BufferedReader reader = Files.newBufferedReader(path)) {
long countPrints = reader
.lines()
.filter(line -> line.contains("print"))
.count();
System.out.println(countPrints);
}
不幸的是你需要显式使用try-with语句来关闭文件流,这会使示例代码有些凌乱。我期待函数式数据流可以在调用类似count和collect时可以自动关闭,因为你不能在相同数据流上调用终止操作两次。
java.util.Random
在Java8中java.util.Random类的一个非常明显的变化就是新增了返回随机数流(random Stream of numbers)的一些方法。
下面的代码是创建一个无穷尽的double类型的数字流,这些数字在0(包括0)和1(不包含1)之间。
Random random = new Random();
DoubleStream doubleStream = random.doubles();
下面的代码是创建一个无穷尽的int类型的数字流,这些数字在0(包括0)和100(不包括100)之间。
Random random = new Random();
IntStream intStream = random.ints(0, 100);
intStream.limit(10).forEach(System.out::println);
示例2: 创建100个随机整数:
List randomBetween0And99 = intStream
.limit(100)
.boxed()
.collect(Collectors.toList());
对于高斯伪随机数(gaussian pseudo-random values)来说,random.doubles()方法所创建的流不能等价于高斯伪随机数,然而,如果用java8所提供的功能是非常容易实现的。
Random random = new Random();
DoubleStream gaussianStream = Stream.generate(random::nextGaussian).mapToDouble(e -> e);
Random random = new Random();
DoubleStream doubleStream = random.doubles(-1.0, 1.0);
LinkedHashMap rangeCountMap = doubleStream.limit(1000000)
.boxed()
.map(Ranges::of)
.collect(Ranges::emptyRangeCountMap, (m, e) -> m.put(e, m.get(e) + 1), Ranges::mergeRangeCountMaps);
rangeCountMap.forEach((k, v) -> System.out.println(k.from() + "\t" + v));
代码的运行结果如下:
-1 49730
-0.9 49931
-0.8 50057
-0.7 50060
-0.6 49963
-0.5 50159
-0.4 49921
-0.3 49962
-0.2 50231
-0.1 49658
0 50177
0.1 49861
0.2 49947
0.3 50157
0.4 50414
0.5 50006
0.6 50038
0.7 49962
0.8 50071
0.9 49695
为了类比,我们再生成一百万个高斯伪随机数:
Random random = new Random();
DoubleStream gaussianStream = Stream.generate(random::nextGaussian).mapToDouble(e -> e);
LinkedHashMap gaussianRangeCountMap =
gaussianStream
.filter(e -> (e >= -1.0 && e < 1.0))
.limit(1000000)
.boxed()
.map(Ranges::of)
.collect(Ranges::emptyRangeCountMap, (m, e) -> m.put(e, m.get(e) + 1), Ranges::mergeRangeCountMaps);
gaussianRangeCountMap.forEach((k, v) -> System.out.println(k.from() + "\t" + v));
附: 完整代码可点击这里获取 https://gist.github.com/bijukunjummen/8129250
译文链接: http://www.importnew.com/9672.html
java.util.Base64
Java8中java.util.Base64性能比较高,推荐使用。请参考:
该类提供了一套静态方法获取下面三种BASE64编解码器:
1)Basic编码: 是标准的BASE64编码,用于处理常规的需求
// 编码
String asB64 = Base64.getEncoder().encodeToString("some string".getBytes("utf-8"));
System.out.println(asB64); // 输出为: c29tZSBzdHJpbmc=
// 解码
byte[] asBytes = Base64.getDecoder().decode("c29tZSBzdHJpbmc=");
System.out.println(new String(asBytes, "utf-8")); // 输出为: some string
2)URL编码: 使用下划线替换URL里面的反斜线“/”
String urlEncoded = Base64.getUrlEncoder().encodeToString("subjects/demo1?abcd".getBytes("utf-8")); System.out.println("Using URL Alphabet: " + urlEncoded); // 输出为: Using URL Alphabet: c3ViamVjdHMvZGVtbzE_YWJjZA==
3)MIME编码: 使用基本的字母数字产生BASE64输出,而且对MIME格式友好: 每一行输出不超过76个字符,而且每行以“\r\n”符结束。
StringBuilder sb = new StringBuilder();
for (int t = 0; t < 10; ++t) {
sb.append(UUID.randomUUID().toString());
}
byte[] toEncode = sb.toString().getBytes("utf-8");
String mimeEncoded = Base64.getMimeEncoder().encodeToString(toEncode);
System.out.println(mimeEncoded);
输出为
ODMzZGM3YWEtNGVjNi00N2UxLTg5ZWQtOTY1YTRkZTgyZGJmZDc3OGJlNjMtYzJhNy00MjI3LTkw
YzYtNDc2NGNmYTk1NTU2ZjY0YjllMzgtZDFlOC00M2MwLWJmNDctODcyNmFmM2I4OWRlZGZmMWMy
MjQtYmM4Ny00Yzg4LTlmMzEtNTRmMzBhYzhmYzYxODdhYWQ4YmYtZjFhMi00MzJiLWJhYjMtOTQ5
ZjlkZmNkMmNmNTFlYzQ4NzItYzNkOC00MWIxLThhN2MtODIxZDM0MzA4OGJmODVjNmVmNmQtMjc3
NS00OTllLWFlZGQtNzIwOWM0NmM3OTE4NTZkYWM0MmEtNDJiZS00NDJjLTgyNDQtNWNhMmJkZDVj
NDUyOGYzMjQyZTctNDA1ZC00ZTlkLTlhNzMtM2ZmMDViYmFhNjEyMGRkMzg1ZWMtMzExZS00MGM1
LTlmNjgtZjcwNTQ5ZGEyODAz
StampedLock
它是java8在java.util.concurrent.locks新增的一个API。ReentrantReadWriteLock 在沒有任何读写锁时,才可以取得写入锁,这可用于实现了悲观读取(Pessimistic Reading),即如果执行中进行读取时,经常可能有另一执行要写入的需求,为了保持同步,ReentrantReadWriteLock 的读取锁定就可派上用场。然而,如果读取执行情况很多,写入很少的情况下,使用 ReentrantReadWriteLock 可能会使写入线程遭遇饥饿(Starvation)问题,也就是写入线程迟迟无法竞争到锁定而一直处于等待状态。StampedLock控制锁有三种模式(写,读,乐观读),一个StampedLock状态是由版本和模式两个部分组成,锁获取方法返回一个数字作为票据stamp,它用相应的锁状态表示并控制访问,数字0表示没有写锁被授权访问。在读锁上分为悲观锁和乐观锁。所谓的乐观读模式,也就是若读的操作很多,写的操作很少的情况下,你可以乐观地认为,写入与读取同时发生几率很少,因此不悲观地使用完全的读取锁定,程序可以查看读取资料之后,是否遭到写入执行的变更,再采取后续的措施(重新读取变更信息,或者抛出异常) ,这一个小小改进,可大幅度提高程序的吞吐量!!
下面是java doc提供的StampedLock一个例子
class Point {
private double x, y;
private final StampedLock sl = new StampedLock();
void move(double deltaX, double deltaY) { // an exclusively locked method
long stamp = sl.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp);
}
}
//下面看看乐观读锁案例
double distanceFromOrigin() { // A read-only method
long stamp = sl.tryOptimisticRead(); //获得一个乐观读锁
double currentX = x, currentY = y; //将两个字段读入本地局部变量
if (!sl.validate(stamp)) { //检查发出乐观读锁后同时是否有其他写锁发生?
stamp = sl.readLock(); //如果没有,我们再次获得一个读悲观锁
try {
currentX = x; // 将两个字段读入本地局部变量
currentY = y; // 将两个字段读入本地局部变量
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
//下面是悲观读锁案例
void moveIfAtOrigin(double newX, double newY) {
// upgrade
// Could instead start with optimistic, not read mode
long stamp = sl.readLock();
try {
while (x == 0.0 && y == 0.0) { //循环,检查当前状态是否符合
long ws = sl.tryConvertToWriteLock(stamp); //将读锁转为写锁
if (ws != 0L) { //这是确认转为写锁是否成功
stamp = ws; //如果成功 替换票据
x = newX; //进行状态改变
y = newY; //进行状态改变
break;
}
else { //如果不能成功转换为写锁
sl.unlockRead(stamp); //我们显式释放读锁
stamp = sl.writeLock(); //显式直接进行写锁 然后再通过循环再试
}
}
} finally {
sl.unlock(stamp); //释放读锁或写锁
}
}
}
小结:
StampedLock要比ReentrantReadWriteLock更加廉价,也就是消耗比较小。
StampedLock与ReadWriteLock性能对比
是和ReadWritLock相比,在一个线程情况下,是读速度其4倍左右,写是1倍。下图是六个线程情况下,读性能是其几十倍,写性能也是近10倍左右:
总结
StampedLock 可以说是Lock的一个很好的补充,吞吐量以及性能上的提升足以打动很多人了,但并不是说要替代之前Lock的东西,毕竟他还是有些应用场景的,起码API比StampedLock容易入手。