线程和进程的区别-通俗版
1. 进程是资源分配的最小单位,线程是CPU调度的最小单位 做个简单的比喻进行来理解: 进程=火车,线程=车厢,线程在进程下行进(单纯的车厢无法运行) 一个进程可以包含多个线程(一辆火车可以有多个车厢) 不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘) 同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易) 进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源) 进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢) 进程可以拓展到多机,进程最多适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上) 进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-”互斥锁” 进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”
快速排序
1. 快速排序123456789101112131415161718192021222324252627282930313233343536373839404142434445 /** * 快速排序 * O(nlogN) 、O(n^2)、O(nlogN) * 空间复杂度 O(logN) * 快速排序是一种高效的排序算法,采用分治法(Divide and Conquer)策略。其核心思想是: * 选择一个基准元素(pivot)。 * 将数组分为两部分:小于基准的元素和大于基准的元素。 * 递归地对两部分进行排序。 * @param arr */public static void quickSort(int[] arr, int left, int right) { if(left >= right){ return; } //// 找到分区点,将数组分为两部分 int partionIndex = partition(arr, left, right); quickSort(arr, left,...
选择排序
1. 选择排序1234567891011121314151617181920212223242526272829303132333435 /** * 选择排序 * O(n^2)、O(n^2)、O(n^2) * 选择排序是一种简单的排序算法, * 其核心思想是每次从未排序部分中选择最小(或最大)的元素, * 将其放到已排序部分的末尾,直到所有元素都排序完毕。 * */public static int[] selectionSort(int[] arr){ int n; if (arr == null || (n = arr.length) <= 1){ return arr; } // 遍历数组,从第一个元素到倒数第二个元素 for(int i = 0; i < n - 1; i++){ // 假设当前索引 i 的元素是最小值 int minIndex = i; // 在未排序部分中查找最小元素的索引 for(int j = i...
插入排序
1. 插入排序1234567891011121314151617181920212223242526272829303132/** * 插入排序 * O(n) 、O(n^2)、O(n^2) * 直接插入排序是一种简单的排序算法, * 其核心思想是将待排序的元素逐个插入到已排序部分的适当位置,直到所有元素都插入完毕 * @param arr * @return */public static int[] insertSort(int[] arr){ int n = 0; if (arr == null || (n = arr.length) <= 1){ return arr; } // 从第二个元素开始遍历(第一个元素默认已排序) for (int i = 1; i < n; i++){ //待插入元素 int key = arr[i]; // 从当前元素的前一个元素开始,向前比较 int j = i - 1; ...
递归实现二分查找
1. 递归实现二分查找123456789101112131415161718192021222324public class BinTree { public static int binaryFind(int[] arr, int target, int i, int j){ if (i > j){ return -1; } int m = (i + j) / 2 ; if(arr[m] < target){ return binaryFind(arr, target, m + 1 , j); }else if(arr[m] > target) { return binaryFind(arr, target, i, m - 1); }else{ return m; } ...
递归处理汉罗塔问题
1. 递归处理汉罗塔问题12345678910111213141516171819202122232425public class Hanota { /** * 汉罗塔问题 */ static void dfs(int n, List<Integer> src, List<Integer> buf, List<Integer> tar){ if (n == 1){ move(src, tar); } // 子问题 f(i-1) :将 src 顶部 i-1 个圆盘借助 tar 移到 buf dfs(n - 1, src, tar, buf); // 子问题 f(1) :将 src 剩余一个圆盘移到 tar move(src, tar); // 子问题 f(i-1) :将 buf 顶部 i-1 个圆盘借助 src 移到 tar dfs(n - 1,...
JVM的类加载器
1. 类加载器层级1.1 自定义加载器(Custom ClassLoader)通过java.lang.ClassLoader的子类自定义的加载class, 属于应用程序根据自身需要定义的加载器。 如:tomcat/jboss都会根据j2ee规范自行实现ClassLoader 1.2 应用类加载器(AppClassLoader)加载classpath指定的jar包,及Djava.class.path所指定目录下的类和jar包 1.3 扩展类加载器(ExtClassLoader)加载Java平台中扩展功能的一些jar包,包括$JAVA_HOME 中jre/lib/ext/*.jar 或 -Djava.ext.dirs 指定目录下的jar包 1.4 启动类加载器(Bootstrap ClassLoader)加载$JAVA_HOME中 jre/lib/rt.jar、resource.jar等里面所有的class 或者 Xbootclasspath 选项指定的jar包 2. 双亲委派机制(父类委托) 向上查找、向下委派 My.class --①加载--> 应用类加载器 ...
JVM的类加载问题
1. 加载.class文件的方式 1)从本地系统中直接加载 2)通过网络下载.class文件 典型场景:Web Applet (小程序应用) 3)从zip,jar等归档文件中加载.class文件 典型场景:后续演变为jar/war格式 4)从专有数据中提取.class文件 典型场景:JSP应用从专有数据库中提取.class文件, 比较少见 5)将Java文件动态编译为.class文件,运行时计算而成 典型场景:动态代理 6)将Java文件动态编译为.class文件,运行时计算而成 典型场景:动态代理 7)从加密文件中获取 典型场景:典型的防Class文件被反编译的保护措施 2. 类加载流程 装载 -> 链接(验证、准备、解析) -> 初始化 所谓的类加载机制就是:虚拟机把Class文件加载到内存并对数据进行校验,转换解析和初始化形成虚拟机可以直接使用的Java类型,即java.lang.Class 2.1 装载(Load) 查找和导入.class文件1)通过一个类的全限定名获取定义此类的二进制字节流2)...
什么时候才会进行垃圾回收
GC 是由JVM自动完成的,根据JMV系统环境而定,所以时机是不确定的 1. 垃圾回收的方式1.1 手动进行回收调用System.gc()方法通知JVM进行一次回收,但是真正是否回收是由JVM控制的 System.gc() 执行的fullGC 1.2 JVM自己回收 (1) 当Eden区或者Survivor区不够用了 –> 触发 youngGC (2) 老年代不够用了 –> 触发 oldGC、MixGC、FullGC (2) 方法区不够用了(matespace区) –> fullGC
String.intern方法
String.intern方法 本地方法,JVM底层实现 作用: 如果字符串常量池中已经包含一个等于此String对象的字符串,则返回池中这个字符串的String对象引用 否则,会将此字符串添加到常量池中,并返回引用