ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

有用的和不为人知的Java特性

2022-01-09 16:31:44  阅读:268  来源: 互联网

标签:Java 不为人知 int System bs2 有用 println new out


在日常java开发中,在jdk8以前 集合的操作比较麻烦,特别是对集合空的判断,需要写一些重复相似的代码去做判断,但是在jdk8以后,concurrent 包下有丰富的集合接口,简化了之前使用集合的复杂度.这里说一些有用的几个特性且容易被忽略的.

 

延迟队列

在开发中如果需要把元素加入到一个队列集合中,但是希望它能够延迟执行,那这里就推荐使用 DelayQueue 队列.这样在加入队列的时候设置这个元素和对象的过期时间就可以了,过期时间到了,就会从队列中出来.这是需要实现Delayed接口,重写掉getDelay和compareTo函数 代码如下:

    @Data
    public static class DelayedEvent implements Delayed {
        private long startTime;
        private String message;

        public DelayedEvent(long startTime, String message) {
            this.startTime = startTime;
            this.message = message;
        }

        @Override
        public long getDelay(TimeUnit unit) {
            long diff = this.startTime - System.currentTimeMillis();
            return unit.convert(diff, TimeUnit.MILLISECONDS);
        }

        @Override
        public int compareTo(Delayed o) {
            return (int) (this.startTime - ((DelayedEvent) o).startTime);
        }
    }

 

调用代码如下,加入元素后,10S之后出对列:

final DelayQueue<DelayedEvent> delayQueue = new DelayQueue<>();
final long timeFirst = System.currentTimeMillis() + 10000;
delayQueue.offer(new DelayedEvent(timeFirst, "hello word"));
log.info("add done");
log.info(delayQueue.take().getMessage());

结果如下:

09:42:02.732 [main] INFO com.test.util.queue - add done
09:42:12.729 [main] INFO com.test.util.queue - hello word

 

时间格式日期

 

在jdk16以前,如果要根据当前时间判断是上午还是下午,那则需要自己实现函数,在函数里面根据一天不同时段做判断,在jdk14以后,可以用下面函数实现:

这样随着时间的变化,返回的时间提示也在动态变化.

String timeHit = DateTimeFormatter.ofPattern("B").format(LocalDateTime.now());
System.out.println(timeHit);

DateTimeFormatter 支持的字母列表如下:

结果返回如下:

 

 

并发累计器

 

又叫做并发加法器, 它允许我们在许多场景中实现一个无锁算法,java concurrent包下提供了LongAccumulator (也有 DoubleAccumulator)使用提供的函数更新值,

在多线程场景下,如果要更新一个int,long 类型的值,通常我们会用AtomicInteger,AtomicLong.给定初始值为10,然后50个线程并发调用,每次累计加1,这里我们直接用 Accumulator 结构实现:

LongAccumulator accumulator = new LongAccumulator(Long::sum, 10);
Runnable w = () -> accumulator.accumulate(1);
ExecutorService executor = Executors.newFixedThreadPool(50);
for (int i = 0; i < 50; i++) {
  executor.submit(w);
}
executor.shutdown();
System.out.println("Balance: " + accumulator.get());

结果返回如下:

 

十六进制格式

 

在jdk8版本中,如果需要在十六进制、字符串、字节 类型相互转换时,转换的操作稍微复杂.在jdk 17版本中提供了HexFormat类来完成这之间的相互转换.代码如下:

HexFormat format = HexFormat.of();
byte[] input = new byte[] {100, 20, -10, 30}; String hex = format.formatHex(input); System.out.println(hex); byte[] output = format.parseHex(hex); assert Arrays.compare(input, output) == 0;

 

 

数组的二分查找

 

在jdk1.8以前,在给定有序数组集合中,查找一个元素的位置,有则返回元素位置,没有返回-1,这需要手动实现二分查找算法,该算法每次把元素近似二分,然后查找.在 jdk1.8 以后提供了底层算法支持,

例如在给定有序集合中,查找元素x,如果x 存在则,返回元素的位置,元素不存在则返回插入该元素的位置索引,代码如下:

 int[] t = new int[]{10, 15, 20, 21};
 int idx = Arrays.binarySearch(t, 15);
 System.out.println("元素位置:" + idx);
 idx = Arrays.binarySearch(t, 100);
 System.out.println("插入元素的位置:" +~idx);

这里补充一下:

取反操作符(~)结论总结:
当n为正数时,~(n) = -(n+1)
当n为负数时,~(-n) = n - 1,忽略负号

结果如下:

 

 

Bit Set

 

如果我们需要对位数组进行操作,一般操作就是我们申明一个boolean 类型的数组,每一位用boolean表示true和fasle(0和1).有了bit set 类,我们就可以申明为 BItSet .该类允许我们存储和操作位的数组。与布尔值[]相比,它消耗的内存要少8倍,我们可以对数组执行逻辑操作,例如,和,或,xor (可以理解就是集合中的 并集 交集 差集),代码如下:

        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);
        bs2.and(bs1);
        System.out.println("and: " + bs2);

 

结果如下:

 

 

 

Phaser

 

phaser 是解决了分阶段的并发问题,与的 countdowlatch 非常相似。然而,它提供了一些额外的功能。它允许我们设置在继续执行之前需要等待的线程的动态数量。使用 Phaser 时,定义的线程数需要在屏障上等待,然后才能进入下一步的执行。正因为如此,我们可以协调多个执行阶段.定义3个线程,这3个线程共同有3个阶段,第一个阶段执行完之后,再执行下一个阶段,代码如下:

Phaser phaser = new Phaser(3);
Runnable r = () -> {
    System.out.println("phase-0");
    phaser.arriveAndAwaitAdvance();
    System.out.println("phase-1");
    phaser.arriveAndAwaitAdvance();
    System.out.println("phase-2");
    phaser.arriveAndDeregister();
};

ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
    executor.submit(r);
}

 

结果如下:

 

Stamped Lock

 

javaconcurrent 是最有趣的java包之一,该包提供了很多多线程下并发使用的工具和集合.该锁的核心思想在于,在读的时候如果发生了写,应该通过重试的方式来获取新的值,而不应该阻塞写操作。这种模式也就是典型的无锁编程思想,和CAS自旋的思想一样。这种操作方式决定了StampedLock在读线程非常多而写线程非常少的场景下非常适用,同时还避免了写饥饿情况的发生.是ReadWriteLock 的替代品,它允许对读操作进行乐观锁定。而且,它的性能比 ReentrantReadWriteLock 更好。 下面的例子是一个多个线程并发执行,其中一个线程是对一个数的自增操作,另外一个线程则是读出最新写入后的结果,其实根据所的特性,读采取乐观锁,理论上不会阻塞写操作.

public class Number {
private int acc;

public Number(int amount) {
this.acc = amount;
}
}

StampedLock lock = new StampedLock(); Number b = new Number(10); Runnable w = () -> { long stamp = lock.writeLock(); b.setAcc(b.getAcc() + 1); System.out.println("acc: " + b.getAcc()); lock.unlockWrite(stamp); }; Runnable r = () -> { long stamp = lock.tryOptimisticRead(); if (!lock.validate(stamp)) { stamp = lock.readLock(); try { System.out.println("read acc: " + b.getAcc()); } finally { lock.unlockRead(stamp); } } else { System.out.println("Optimistic read fails"); } }; ExecutorService executor = Executors.newFixedThreadPool(10); for (int i = 0; i < 10; i++) { executor.submit(w); executor.submit(r); }

下面的直接结果在没台机器上执行的过程可能不太一样,这取决于cpu的速速,但是最终结果一定是20.

执行结果如下:

 

标签:Java,不为人知,int,System,bs2,有用,println,new,out
来源: https://www.cnblogs.com/stackflow/p/15780365.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有