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

你可能不知道但有用的Java特性

时间:2023-03-17 12:33:28 科技观察

本文转载自微信公众号“crossoverJie”,作者crossoverJie。转载本文请联系crossoverJie公众号。在本文中,您将学习一些您可能没有听说过的有用的Java特性,这些是我个人使用或从其他文章中学到的一些特性,重点是API而不是语言本身。延迟队列我们都知道Java中有很多类型的集合可用,但是你听说过DelayQueue吗?它是一种特定类型的集合,允许我们根据延迟时间对数据进行排序。这是一个非常有趣的课程。它实现了BlockingQueue接口,数据只有过期才能从队列中取出。使用它的第一步是你的类需要在Delayed接口中实现getDelay方法。当然,你也可以不声明一个类就使用Record。这是Java14的新特性publicrecordDelayedEvent(longstartTime,Stringmsg)implementsDelayed{publiclonggetDelay(TimeUnitunit){longdiff=startTime-System.currentTimeMillis();returnunit.convert(diff,TimeUnit.MILLISECONDS);}publicintcompareTo(Delayedo){return(int))(this.startTime-((DelayedEvent)o).startTime);}}假设我们需要一个延迟10s获取的数据,我们只需要放入一个比当前时间长10s的任务。finalDelayQueuedelayQueue=newDelayQueue<>();finallongtimeFirst=System.currentTimeMillis()+10000;delayQueue.offer(newDelayedEvent(timeFirst,"1"));log.info("Done");log.info(delayQueue.take().msg());最终输出如下:Dateintimeformat这个功能可能对大部分人用处不大,但说实话,我个人非常喜欢;不管怎样,Java8在时间API上改进了很多。从这个版本开始,您可能不需要任何其他扩展。你能想到吗,从Java16开始你甚至可以使用标准库来表达一天内的日期,比如“早上”“下午”,这是一种新的格式语句B.Strings=DateTimeFormatter.ofPattern("B").format(LocalDateTime.now());System.out.println(s);下面是我的输出,跟你现在的时间有关。您可能想知道为什么它会调用“B”。这似乎并不直观。下表或许可以解答你的疑惑:开发者很少掌握它,尤其是长期使用web开发框架的开发者。有多少人用过Lock?与synchronized相比,这是一种更加灵活的线程同步机制。从Java8开始你可以使用一个新的锁:StampedLock.StampedLock,它可以替代ReadWriteLock。假设现在有两个线程,一个线程更新金额,另一个线程读取余额;更新余额的线程首先需要读取金额,在多线程的情况下,需要某种同步机制(否则更新数据时会出错),第二个线程使用乐观锁读取余额。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();if(!lock.validate(stamp)){stamp=lock.readLock();try{System.out.println("Read:"+b.getAmount());}finally{lock.unlockRead(stamp);}}else{System.out.println("Optimisticreadfails");}};现在用50个线程测试update和read,最终余额会等于60000.ExecutorServiceexecutor=Executors.newFixedThreadPool(10);for(inti=0;i<50;i++){executor.submit(w);executor.submit(r);}并发累加器锁并不是并发包中唯一有趣的特性,并发累加器也很有趣;它可以根据我们提供的功能更新数据;在多线程更新数据的场景下,LongAccumulator是比AtomicLong更好的选择。现在让我们看看如何使用它。我们需要两个参数进行初始化;第一个是用于累加计算的函数,通常是求和函数,第二个参数是累加计算的初始化值。接下来我们以10000为初始值创建一个LongAccumulator,最后的结果是什么?其实结果和上面一样,都是60000,只是这次我们没有使用锁。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());assertbalance.get()==60000L;数组二分查找假设我们要在一个排序列表中插入一个新元素,我们可以使用Arrays.binarySearch()函数,当key存在时,返回key所在的索引,如果不存在,它将返回插入的位置-(插入点)-1。binarySearch是Java中非常简单高效的查询方法。在下面的示例中,可以通过取反返回结果来到达索引位置。int[]t=newint[]{1,2,4,5};intx=Arrays.binarySearch(t,3);assert~x==2;负数的二进制由正数的补码表示。数取反+1等于补码,所以这里直接取反等于Arrays.binarySearch()不存在时的返回值。BitSet如果需要对二进制数组进行操作怎么办?使用boolean[]布尔数组?有一种更高效、更节省内存的方式,那就是BitSet。它允许我们存储和操作位数组,与boolean[]相比可以节省8倍的内存;也可以使用诸如and/or/xor之类的逻辑运算。假设我们现在有两个位数组,我们需要对它们进行异或运算;我们需要创建两个BitSet实例,然后调用xor函数。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("xor:"+bs2);最终输出如下: