当前位置: 首页 > 科技观察

有用但鲜为人知的Java特性_0

时间:2023-03-17 00:15:46 科技观察

在本文中,您可以了解一些您可能没有听说过的有用的Java特性。这是我最近使用过的个人功能列表,或者是我在阅读有关Java的文章时偶然发现的。我不会关注语言方面,而是关注API。1.延迟队列[DelayQueue]众所周知,Java中有多种类型的集合可供使用。但是你听说过DelayQueue吗?它是一种特定类型的Java集合,允许我们根据元素的延迟对元素进行排序。老实说,这是一个非常有趣的课程。虽然DelayQueue类是JavaCollections的成员,但它属于java.util.concurrent包。它实现了BlockingQueue接口。一个元素只有在其时间到期时才能从队列中取出。为了使用它,首先需要在类中的接口中实现getDelay方法Delayed。它不必是一个类——您也可以使用JavaRecord。publicrecordDelayedEvent(longstartTime,Stringmsg)implementsDelayed{publiclonggetDelay(TimeUnitunit){longdiff=startTime-System.currentTimeMillis();返回unit.convert(diff,TimeUnit.MILLISECONDS);}publicintcompareTo(Delayedo){return(int)(this.startTime-((DelayedEvent)o).startTime);}}假设我们要将元素延迟10秒。我们只需要在DelayedEvent类中将当前时间设置为10秒即可。finalDelayQueuedelayQueue=newDelayQueue<>();finallongtimeFirst=System.currentTimeMillis()+10000;delayQueue.offer(newDelayedEvent(timeFirst,"1"));log.info("Done");log.info(delayQueue.take().msg());上面可见代码的输出是什么?让我们来看看。2.时间格式的天数[PeriodofDaysinTimeFormat]Java8改进了许多时间处理API。从这个版本的Java开始,在大多数情况下可能不需要使用任何额外的库,例如JodaTime。你能想象从Java16开始,你甚至可以在一天中的时间使用标准格式化程序,比如“早上”或“下午”吗?有一种名为B的新格式模式。Strings=DateTimeFormatter.ofPattern("B").format(LocalDateTime.now());System.out.println(s);这是我的结果。当然,结果取决于一天中的时间。3.StampedLock[StampedLock]JavaConcurrent是最有趣的Java包之一。同时,它也是开发人员不太了解的一种,尤其是如果他们主要使用Web框架。你们中有多少人曾经在Java中使用过锁?锁是比“同步”块更灵活的线程同步机制。从Java8开始,您可以使用一种称为“StampedLock”的新型锁。StampedLock是使用ReadWriteLock的替代方法。它允许对读取操作进行乐观锁定。此外,它具有比ReentrantReadWriteLock更好的性能。假设我们有两个线程。其中第一个更新余额,而第二个读取余额的当前值。为了更新余额,我们当然需要先读取它的当前值。我们在这里需要某种同步,假设第一个线程同时运行多次。第二个线程只是说明了如何对读取操作使用乐观锁定。StampedLocklock=newStampedLock();Balanceb=newBalance(10000);Runnablew=()->{longstamp=lock.writeLock();b.setAmount(b.getAmount()+1000);System.out.println("写入:"+b.getAmount());lock.unlockWrite(stamp);};Runnabler=()->{longstamp=lock.tryOptimisticRead();如果(!lock.validate(stamp)){stamp=lock.readLock();try{System.out.println("读取:"+b.getAmount());}最后{lock.unlockRead(stamp);}}else{System.out.println("乐观读取失败");}};现在,让我们通过同时运行两个线程50次来对此进行测试。它应该按预期工作——余额的最终值为60000。executor.submit(r);}4.并行累加器[Concurrentaccumulators]锁定和合并这不是JavaConcurrent包中唯一有趣的特性。另一个称为并发累加器。还有并发加法器,但它是一个非常相似的功能。LongAccumulator(还有DoubleAccumulator)使用提供的函数更新值。它允许我们在很多场景下实现无锁算法。AtomicLong通常最适合在多个线程更新一个公共值时使用。让我们看看它是如何工作的。为了创建它,您需要在构造函数中设置两个参数。第一个是用于计算累加结果的函数。通常,您会使用sum方法。第二个参数表示累加器的初始值。现在,让我们创建一个初始值为10000的LongAccumulator并从多个线程调用accumulate()方法。最后的结果是什么?LongAccumulatorbalance=newLongAccumulator(Long::sum,10000L);Runnablew=()->balance.accumulate(1000L);ExecutorServiceexecutor=Executors.newFixedThreadPool(50);for(inti=0;i<50;i++){executor.submit(w);}executor.shutdown();if(executor.awaitTermination(1000L,TimeUnit.MILLISECONDS))System.out.println("Balance:"+balance.get());断言余额。得到()==60000L;5.十六进制格式[HexFormat]有时我们需要在十六进制格式的字符串、字节或字符之间进行转换。从Java17开始,您可以使用HexFormat类。只需创建一个HexFormat实例,然后您就可以将例如输入字节表格式化为十六进制字符串。你也可以,例如将输入的十六进制字符串解析为字节表,如下所示。HexFormatformat=HexFormat.of();byte[]input=newbyte[]{127,0,-50,105};Stringhex=format.formatHex(input);System.out.println(hex);byte[]output=format.parseHex(hex);assertArrays.compare(input,output)==0;6。BinarySearchforArrays【数组二分查找】假设我们要插入一个新的元素到已排序的表中。Arrays.binarySearch()返回搜索键的索引(如果包含)。否则,它返回第i个插入点,我们可以使用它来计算新键的索引:-(insertionpoint)-1。此外,binarySearch方法是在Java中查找排序数组中元素的最简单和最有效的方法。一个例子如下,我们有一个输入表,其中有四个元素按升序排列。我们要将数字“3”插入到该表中。这就是我们如何计算插入点的索引。int[]t=newint[]{1,2,4,5};intx=Arrays.binarySearch(t,3);断言~x==2;7。Bitmap[BitSet]如果我们需要对位数组进行一些操作呢?你会为此使用``boolean[]吗?有一种更有效和内存效率更高的方法来做到这一点。它是BitSet类。BitSet类允许我们存储和操作位数组。与boolean[]相比,它消耗的内存少8倍。我们可以对数组执行逻辑运算,例如and、or、xor`。假设我们有两个输入位数组。我们想对它们执行异或运算。事实上,只返回那些只插入一个数组而不是另一个数组的元素的操作。为此,我们需要创建两个“BitSet”实例并在其中插入示例元素,如下所示。最后,应该在其中一个BitSet实例上调用xor方法。它需要第二个BitSet实例作为参数。BitSetbs1=newBitSet();bs1.set(0);bs1.set(2);bs1.set(4);System.out.println("bs1:"+bs1);BitSetbs2=newBitSet();bs2.set(1);bs2.set(2);bs2.set(3);System.out.println("bs2:"+bs2);bs2.xor(bs1);System.out.println("异或:“+bs2);这是运行上面可见的代码后的结果。8.Phaser[Phaser]和这里的其他一些例子一样,它也是JavaConcurrent包的一个元素。它被称为“移相器”。它与广为人知的CountDownLatch非常相似。但是,它提供了一些附加功能。它允许我们设置在继续执行之前需要等待的动态线程数。使用“Phaser”,定义数量的线程需要在屏障上等待,然后才能继续执行下一步。因此,我们可以协调多个执行阶段。在下面的示例中,我们设置了50个线程的障碍,以便在进入下一阶段执行之前达到。然后我们创建一个线程并在Phaser实例上调用方法arriveAndAwaitAdvance()。它会阻塞线程,直到所有50个线程都不会到达屏障。然后它进入阶段1并调用方法arriveAndAwaitAdvance()。Phaserphaser=newPhaser(50);Runnabler=()->{System.out.println("phase-0");phaser.arriveAndAwaitAdvance();System.out.println("第一阶段");移相器.arriveAndAwaitAdvance();System.out.println("第二阶段");phaser.arriveAndDeregister();};ExecutorServiceexecutor=Executors.newFixedThreadPool(50);for(inti=0;i<50;i++){executor.submit(r);}这是执行上面可见代码后的结果.