HashSet 、 LinkedHashSet 和 TreeSet 的主要区别在于底层数据结构不同。
HashSet 的底层数据结构是哈希表(基于 HashMap 实现)。
LinkedHashSet 的底层数据结构是链表和哈希表,元素的插⼊和取出顺序满⾜ FIFO。
TreeSet 底层数据结构是红⿊树,元素是有序的。
多线程的使用
- 继承Thread类 (可以说是 将任务和线程合并在一起)
- 实现Runnable接口 (可以说是 将任务和线程分开了)
- 实现Callable接口 (利用FutureTask执行任务)
继承Thread类
//继承Thread实现多线程
class Thread1 extends Thread{
private String name;
public Thread1(String name) {
this.name=name;
}
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + "运行 : " + i);
try {
sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
Thread1 mTh1=new Thread1("A");
Thread1 mTh2=new Thread1("B");
mTh1.start();
mTh2.start();
}
}
上面这个两个类,Thread1
类继承了Thread父类,并重写了里面的run
方法。实现了多线程里面的方法,并在main函数中进行实例化了两个mTh1
,mTh2
两个线程。
线程的⽣命周期和状态
NEW: 初始状态,线程被创建出来但没有被调⽤
RUNNABLE: 运⾏状态,线程被调⽤了 start() 。 start() 等待运⾏的状态。
BLOCKED :阻塞状态,需要等待锁释放。
WAITING:等待状态,表示该线程需要等待其他线程做出⼀些特定动作(通知或中断)。 TIME_WAITING:超时等待状态,可以在指定的时间后⾃⾏返回⽽不是像 WAITING 那样⼀直等 待。
TERMINATED:终⽌状态,表示该线程已经运⾏完毕
避免死锁的方法
1、银行家算法
银行家算法是一种死锁避免的算法,它的设计灵感来自于银行业的贷款和存款模型。在银行家算法中,系统被视为一个银行,进程被视为银行的客户,而资源则被视为银行的资金。这个类比帮助理解了资源的分配和使用过程,以及系统如何避免死锁的发生。
关系:
-
资源分配类比: 在银行家算法中,系统中的资源被视为银行的资金或贷款,进程对资源的请求则类比为客户对银行的贷款请求。
-
资源请求和释放: 进程在执行过程中会请求资源,就像客户向银行申请贷款一样。当进程完成任务后,会释放已经使用的资源,类似于客户还清贷款后归还资金给银行。
-
安全性检查: 银行家算法通过模拟系统资源分配的过程来检查系统是否处于安全状态,即是否存在一种资源分配顺序,使得所有进程都能完成并释放资源,类似于银行在进行贷款审批前的风险评估。
-
资源足够性: 银行家算法要求在分配资源时,系统必须确保分配后还有足够的资源供其他进程使用,类似于银行在进行贷款审批时需要确保自身还有足够的资金供其他客户使用,以避免银行出现资金短缺的情况。
2、时间戳算法(Timestamp-based Algorithms)
时间戳算法基于全局的时间戳来分配资源和管理进程的请求。每个进程和资源都有一个唯一的时间戳,系统根据时间戳的顺序来决定资源的分配和进程的执行顺序。这种算法可以避免死锁,但可能会增加系统的开销。
Java虚拟机(JVM)的垃圾回收机制是指在Java程序运行过程中,自动识别并回收不再被程序使用的内存空间的过程。Java语言中的垃圾回收机制是一种自动化的内存管理方式,开发者不需要显式地去释放内存,而是由JVM自动管理。以下是JVM垃圾回收机制的一般原理和常见的回收算法:
原理:
-
对象生命周期管理: Java中的对象在被创建后,会被JVM自动跟踪管理其生命周期。当对象不再被引用时,它会变得不可达(即没有任何引用指向它),成为垃圾。
-
垃圾识别: 垃圾回收器负责识别不再被引用的对象,并将其标记为垃圾。
-
垃圾回收: JVM中的垃圾回收器会定期或在内存达到一定阈值时,自动回收被标记为垃圾的对象所占用的内存空间,将其释放出来供后续的对象使用。
垃圾回收算法:
-
标记-清除算法(Mark and Sweep): 首先标记所有活跃对象,然后清除所有未标记的对象,释放它们所占用的内存空间。这种算法会产生内存碎片,可能导致内存碎片化。
-
复制算法(Copying): 将内存空间分为两个区域,一半用于存放对象,另一半保持空闲。当一个区域被占满后,将存活的对象复制到另一个区域,并清除原区域中的所有对象。这种算法解决了内存碎片化的问题,但会浪费一部分内存空间。
-
标记-整理算法(Mark and Compact): 结合了标记-清除和复制算法的优点。首先标记活跃对象,然后将活跃对象向一端移动,之后清理出空闲内存。这种算法避免了内存碎片化,并减少了内存移动的次数。
-
分代回收算法(Generational Garbage Collection): 将堆内存分为不同的代(Generation),一般分为新生代(Young Generation)和老年代(Old Generation)。新生代中的对象生命周期短,老年代中的对象生命周期长。根据代的特性采用不同的垃圾回收算法和策略。
锁
-
数据一致性: 在分布式环境中,各个节点的计算结果需要合并以得到全局的最优解。如果多个节点在没有同步的情况下同时修改共享状态,可能会导致最终的结果不一致。通过使用锁来控制对共享状态的访问,可以确保数据的一致性。
-
部分计算依赖: 在一些情况下,某些节点的计算结果可能依赖于其他节点的计算结果。使用锁可以确保节点之间的计算顺序,保证依赖关系的正确性。