关于#Java#的问题,如何解决?

请问Java分秒的加减怎实现例如4分30秒减去4分钟,得出秒数,谢谢

可以使用 Duration,把时间转为long ,然后相减

 public static void main(String[] args) {
        int minutes = 4;
        int seconds = 30;
        int subtractMinutes = 4;
        // 将4分30秒转换成秒数
        long totalSeconds = Duration.ofMinutes(minutes).plusSeconds(seconds).getSeconds();
        // 将4分钟转换成秒数
        long subtractSeconds = Duration.ofMinutes(subtractMinutes).getSeconds();
        long resultSeconds = totalSeconds - subtractSeconds;
        System.out.println("4分30秒减去4分钟等于:" + resultSeconds + "秒");


    }

  • 这个问题的回答你可以参考下: https://ask.csdn.net/questions/7482396
  • 你也可以参考下这篇文章:【Java】给定一个有序整数数组,找出其中两个数相加等于目标值,返回下标值
  • 同时,你还可以查看手册:java-学习Java语言 - 描述Java编程语言的基本概念和特点的课程。-泛型是Java编程语言的一个强大功能。它们提高了代码的类型安全性,使更多的错误可以在编译时发现。-野生动物 中的内容
  • 除此之外, 这篇博客: 关于Java堆内存是线程共享的吗?中的 Java对象的内存分配过程是如何保证线程安全的? 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • Java是一门面向对象的语言,我们在Java要使用的对象都需要被创建出来,在Java中,创建一个对象的方法有很多种,但是无论如何,对象在创建过程中,都需要进行内存分配。

    对象的内存分配过程中,主要是对象的引用指向这个内存区域,然后进行初始化操作。

    但是,因为堆是全局共享的,因此在同一时间,可能有多个线程在对上申请空间,那么,在并发场景中,如果两个线程先后把对象引用指向了同一个内存区域,会发生什么呢?

    在这里插入图片描述
    为了解决这个并发问题,对象的内存分配过程就必须进行同步控制。但是我们知道,无论是使用哪种同步方案(实际上虚拟机使用的可能是CAS),都会影响内存的分配效率。

    而Java对象的分配是Java中等的高频操作,所以,人们想到另外一个办法来提升效率,这里着重说一个HotSpot虚拟机的方案:

    每个线程在Java堆中预先分配一小块内存,然后再给对象分配内存的时候,直接在自己这块”私有”内存中分配,当这部分区域用完之后,再分配新的”私有”内存。

    这种方案被称之为TLAB分配,即Thread Local Allocation Buffer。这部分Buffer是从堆中划分出来的,但是是本地线程独享的。

    什么是TLAB?

    TLAB是虚拟机在堆内存的eden划分出来的一块专用空间,是线程专属的。在虚拟机的TLAB功能启动的情况下,在线程初始化时,虚拟机为每个线程分配拟一块TLAB空间,只给当前线程使用,这样每个线程都单独拥有一个空间,如果需要分配内存,就在自己的空间上分配,这样就不存在竞争的情况,可以大大提升分配效率。

     注意到上面的描述中"线程专属"、"只给当前线程使用"、"每个线程单独拥有"的描述了吗?
    

    所以说,因为有了TLAB技术,堆内存并不是完完全全的线程共享,其eden区域中还是有一部分空间是分配给线程独享的。

    这里值得注意的是,我们说TLAB是线程独享的,但是只是在**“分配”这个动作上是线程独占的**,至于在读取、垃圾回收等动作上都是线程共享的。而且在使用上也没有什么区别。

    在这里插入图片描述

    也就是说,虽然每个线程在初始化时都会去堆内存中申请一块TLAB,并不是说这个TLAB区域的内存其他线程就完全无法访问了,其他线程的读取还是可以的,只不过无法在这个区域中分配内存而已。

    并且,在TLAB分配之后,并不影响对象的移动和回收,也就是说,虽然对象刚开始可能通过TLAB分配内存,存放在Eden区,但是还是会被来及回收或者被移到Survivor Space ,Old Gen等。

    在这里插入图片描述
    还有一点需要注意的是,我们说TLAB是在eden区分配的,因为eden区域本身就不太大,而且TLAB空间的内存也非常小,默认情况下仅占有整个Eden空间的1%。所以,必然存在一些大对象是无法在TLAB直接分配。

    遇到TLAB中无法分配的大对象,对象还是可能在eden区或者老年代等进行分配的,但是这种分配就需要进行同步控制,这也是为什么我们经常说:小的对象比大的对象分配起来更加高效。

    TLAB带来的问题

    虽然在一定程度上,TLAB大大的提升了对象的分配速度,但是TLAB并不是就没有任何问题的。

    前面我们说过,因为TLAB内存区域并不是很大,所以,有可能会经常出现不够的情况。

    比如一个线程的TLAB空间有100KB,其中已经使用了80KB,当需要再分配一个30KB的对象时,就无法直接在TLAB中分配,遇到这种情况时,有两种处理方案:
    1、如果一个对象需要的空间大小超过TLAB中剩余的空间大小,则直接在堆内存中对该对象进行内存分配。
    2、如果一个对象需要的空间大小超过TLAB中剩余的空间大小,则废弃当前TLAB,重新申请TLAB空间再次进行内存分配。

    以上两个方案各有利弊,如果采用方案1,那么就可能存在着一种极端情况,就是TLAB只剩下1KB,就会导致后续需要分配的大多数对象都需要在堆内存直接分配。

    如果采用方案2,也有可能存在频繁废弃TLAB,频繁申请TLAB的情况,而我们知道,虽然在TLAB上分配内存是线程独享的,但是TLAB内存自己从堆中划分出来的过程确实可能存在冲突的,所以,TLAB的分配过程其实也是需要并发控制的。而频繁的TLAB分配就失去了使用TLAB的意义。

    为了解决这两个方案存在的问题,虚拟机定义了一个refill_waste的值,这个值可以翻译为“最大浪费空间”。

    当请求分配的内存大于refill_waste的时候,会选择在堆内存中分配。若小于refill_waste值,则会废弃当前TLAB,重新创建TLAB进行对象内存分配。
    前面的例子中,TLAB总空间100KB,使用了80KB,剩余20KB,如果设置的refill_waste的值为25KB,那么如果新对象的内存大于25KB,则直接堆内存分配,如果小于25KB,则会废弃掉之前的那个TLAB,重新分配一个TLAB空间,给新对象分配内存。

    TLAB使用的相关参数

    TLAB功能是可以选择开启或者关闭的,可以通过设置-XX:+/-UseTLAB参数来指定是否开启TLAB分配。

    TLAB默认是eden区的1%,可以通过选项-XX:TLABWasteTargetPercent设置TLAB空间所占用Eden空间的百分比大小。

    默认情况下,TLAB的空间会在运行时不断调整,使系统达到最佳的运行状态。如果需要禁用自动调整TLAB的大小,可以使用-XX:-ResizeTLAB来禁用,并且使用-XX:TLABSize来手工指定TLAB的大小。
    TLAB的refill_waste也是可以调整的,默认值为64,即表示使用约为1/64空间大小作为refill_waste,使用参数:-XX:TLABRefillWasteFraction来调整。
    如果想要观察TLAB的使用情况,可以使用参数-XX+PringTLAB 进行跟踪。

    总结

    为了保证对象的内存分配过程中的线程安全性,HotSpot虚拟机提供了一种叫做TLAB(Thread Local Allocation Buffer)的技术

    在线程初始化时,虚拟机会为每个线程分配一块TLAB空间,只给当前线程使用,当需要分配内存时,就在自己的空间上分配,这样就不存在竞争的情况,可以大大提升分配效率。

    所以,“堆是线程共享的内存区域”这句话并不完全正确,因为TLAB是堆内存的一部分,他在读取上确实是线程共享的,但是在内存分分配上,是线程独享的。

    TLAB的空间其实并不大,所以大对象还是可能需要在堆内存中直接分配。那么,对象的内存分配步骤就是先尝试TLAB分配,空间不足之后,再判断是否应该直接进入老年代,然后再确定是再eden分配还是在老年代分配。

    在这里插入图片描述

  • 您还可以看一下 多编程老师的2021版Java线程池教程课程中的 什么是线程池?为什么要使用线程池?小节, 巩固相关知识点