Java

Java Swing类库汇总

Manan
Swing类库汇总 🌸Look And Feel 外观 BeautyEye L&F(国人开发) 🏠 HomePage: http://docs.52im.net/extend/docs/src/beautyeye3/ 🛒 Github: https://github.com/JackJiang2011/beautyeye 📦 Maven Repository: ❎ Flat L&F 🏠 HomePage: https://www.formdev.com/flatlaf/ 🛒 Github: https://github.com/JFormDesigner/FlatLaf 📦 Maven Repository: ✅ Dark L&F 🏠 HomePage: https://weisj.github.io/darklaf-docs/ 🛒 Github: https://github.com/bulenkov/Darcula 📦 Maven Repository: ✅ Substance L&F 🏠 HomePage: ❌ 🛒 Github: https://github.com/kirill-grouchnikov/radiance 📦 Maven Repository: ✅ Web L&F 🏠 HomePage: http://weblookandfeel.com/ 🛒 Github: https://github.com/mgarin/weblaf 📦 Maven Repository: ✅ 🛠实用工具类库

将Java编译为本地代码

Manan
将Java编译为本地代码 通常Java程序的执行流程为:将Java代码编译为Byte Code(字节码),然后JVM执行引擎执行编译好的Byte Code。这是一种中间语言的特性,它的好处就是可以做到平台的无关性,一份代码可以在任意的平台上运行。而且JVM语言采用了JIT(Just In Time)即时编译技术,会将执行中的热点代码(字节码)编译为本地代码运行,提高代码执行性能。 虽然Java的这种中间语言+即时编译的技术有很多优点,同时也有很多缺点。比如JVM执行引擎执行会比较占用资源,而且JIT有热加载的问题,所以执行的性能发挥不太稳定。对于软件的发布来说,我们通常会将JRE连同我们的应用程序一同发布,这样虽然能解决用户PC上JRE版本与要求版本不一致问题,但是也增大了软件包的体积。 针对上述JVM存在的问题,Oracel公司推出了一个名为GraalVM的项目,这个项目可以将Java字节码编译为本地代码。编译生成的本地代码无须JVM,可以直接在目标机器上运行。而且这种AOT(Ahead Of Time)的编译方式并不会对性能造成太大的影响,同时它还能够减少运行时的内存占用与CPU资源消耗。具体的其他特性,可以查看GraalVM官网。 GraalVM安装(OSX) GraalVM JDK可以与你本机的JDK互补的存在,GraalVM并没有提供相应的安装程序,而是以压缩的包的形式进行发布,你可以从Github上进行下载:https://github.com/graalvm/graalvm-ce-builds/releases/tag/vm-21.0.0.2 下载完毕后解压缩至相应目录即可。 安装完毕GraalVM之后,你可以安装native-image本地代码编译工具,这个工具需要依赖于GraalVM,所以在安装这个工具前,请先安装GraalVM。native-image本地代码编译工具也可以在上文中的Github仓库中进行下载,它也是压缩包的形式进行发布的,下载下来解压即可。但是与GraalVM不同的是这个工具并不是开箱即用,而是需要一些配置。 sudo xattr -r -d com.apple.quarantine /path/to/GRAALVM_HOME <GraalVM安装目录>/Contents/Home/bin/gu install native-image 执行完这个命令后,native-image就会安装到GraalVM的bin目录下。 测试编译本地代码 Java源代码: public class Test { public static void main(String... args) { System.out.println("Hello world"); } } 将源代码编译为字节码: javac Test.java 将字节码编译为本地代码: native-image Test

Java volatile关键字作用

Manan
概述 volatile是java中的一个关键字,用于修饰变量。被此关键修饰的变量可以禁止对此变量操作的指令进行重排,还有保持内存的可见性。 简言之它的作用就是: 禁止指令重排 保持内存的可见性 禁止指令重排 CPU在执行代码时,为了提高执行效率,有时会将代码乱序执行。但是乱序也不是随随便便的乱序,而是在一定规则下,对指令进行重排然后执行。指令重排在单线程下没有什么问题,但是在多线程环境下容易造成并发安全问题。 保持内存的可见性 何谓之内存的可见性,其实笔者在Java线程安全问题一文中对此问题进行过阐述。线程是一种资源,线程在执行代码时有自己的工作内存(线程执行的堆栈)。一般来说,一些共享变量存在于堆内存中,线程对于共享变量的操作实际上对自己工作内存中共享变量的副本进行操作,线程并不会直接操作堆内存的中的共享变量。 这里我们可以通过字节码进行验证,首先我们的java源代码是对变量a进行一个自增操作 => a++,而其对应的字节码为: getstatic #2 iconst_1 iadd putstatic #2 return 从字节码层面可以体现出工作内存与主存。但是,Java内存可见性的控制并不是在字节码层面,而是在JVM层面。即使你对变量a使用volatile修饰,那么编译之后的字节码也没有变化。 JVM定义了对于内存的8种操作: 这些操作是JVM层面的操作,在Java源代码中并不能感受到。线程在所操作的共享变量,其实是存在于自己线程栈中的变量副本。在多线程并发的情况下,如果有其他线程修改了主存中的值,那么其他线程无法感知这种修改。因为线程在通过READ-LOAD操作拷贝完副本之后,之后线程对于数据的操作都是对于副本进行的。这也就是内存可见性的问题的来源,简言之就是线程之间无法感知对于主存共享变量的修改。 volatile关键字就是解决了这样的问题,使得线程对于主存具有一定的可见性。其解决方案是对于volatile标注的变量,每次在使用之前都要重新从主存中加载,同时每次对于变量完成修改后,要及时的将变量写回主存。 通过这样的操作,每个线程就能感知到内存中变量的变化,并及时更新自己副本的值。不过需要注意的是,volatile关键字并不能保障并发的安全性。尽管每次在使用之前都会更新值,但是这并没有解决变量访问的有序性。所以在高并发场景下依然会出现问题。

Java线程池内部原理

Manan
概述 ThreadPoolExecutor是Java语言对于线程池的实现。池化技术是一种复用资源,减少开销的技术。线程是操作系统的资源,线程的创建与调度由操作系统负责,线程的创建与调度都要耗费大量的资源,其中线程创建需要占用一定的内存,而线程的调度需要不断的切换线程上下文造成一定的开销。同时线程执行完毕之后就会被操作系统回收,这样在高并发情况下就会造成系统频繁创建线程。 为此线程池技术为了解决上述问题,使线程在使用完毕后不回收而是重复利用。如果线程能够复用,那么我们就可以使用固定数量的线程来解决并发问题,这样一来不仅节约了系统资源,而且也会减少线程上下文切换的开销。 参数 ThreadPoolExecutor的构造函数有7个,它们分别是: corePoolSize(int):线程池的核心线程数量 maximumPoolSize(int):线程池最大线程数量 keepAliveTime(long):保持线程存活的时间 unit(TimeUnit):线程存活时间单位 workQueue(BlockingQueue):工作队列,用于临时存放提交的任务 threadFactory(ThreadFactory):线程工厂,用于创建线程 handler(RejectedExecutionHandler):任务拒绝处理器,当线程池无法再接受新的任务时,会交给它处理 一般情况下,我们只使用前五个参数,剩余两个我们使用默认参数即可。 任务提交逻辑 其实,线程池创建参数都与线程池的任务提交逻辑密切相关。根据源码描述可以得知:当提交一个新任务时(执行线程池的execute方法)会经过三个步骤的处理。 当任务数量小于corePoolSize时,线程池会创建一个新的线程(创建新线程由传入参数threadFactory完成)来处理任务,哪怕线程池中有空闲线程,依然会选择创建新线程来处理。 当任务数量大于corePoolSize时,线程池会将新任务压入工作队列(参数中传递的workQueue)等待调度。 当新提交的任务无法压入工作队列时,会检查当前任务数量是否大于maximumPoolSize。如果小于maximunPoolSize则会新建线程来处理任务(这时我们的keepAliveTime参数就起作用了,它主要作用于这种情况下创建的线程,如果任务数量减小,这些线程闲置了,那么在超过keepAliveTime时间后就会被回收)。如果大于了maximumPoolSize就会交由任务拒绝处理器handler处理。 线程池状态 正如线程有不同的状态一样,线程池也拥有不同的运行状态。源码中提出,线程池有五种状态,分别为: RUNNING:运行状态,不断接收任务并处理它们。 SHUTDOWN:关闭状态,不接收新任务,但是会处理工作队列中排队的任务。 STOP:停止状态,不接收新任务,清空工作队列且不会处理工作队列的任务。 TIDYING:待终止状态,此状态下,任务队列和线程池都为空。 TERMINATED:终止状态,线程池关闭。 如何让线程不被销毁 文章开头说到,线程在执行完毕之后会被操作系统回收销毁,那么线程池时如何保障线程不被销毁?首先看一个测试用例: public static void testThreadState() { Thread thread = new Thread(() -> System.out.println("Hello world")); // 创建一个线程 System.out.println(thread.getState()); // 此时线程的状态为NEW thread.start(); // 启动线程,状态为RUNNING System.out.println(thread.getState()); try { thread.join(); System.out.println(thread.getState()); // 线程运行结束,状态为TERMINATED thread.start(); // 此时再启动线程会发生什么呢? } catch (InterruptedException e) { e.