1.HashSet 和HashMap 的区别?
顶层接口不同,HashSet是实现Collection接口的下的Set接口只存储对象,而HashMap实现Map接口,用于存储键值对。
HashSet 底层是用 HashMap 存储的,HashSet 封装了一系列 HashMap 的方法,HashSet 将(自己的)值保存到 HashMap 的 Key 里面了。
2.run()和start()的区别?
3.Java有哪些常用锁?
Java 提供了多种锁机制以解决多线程同步问题,常见的有:
- 内置锁(synchronized):简单易用,适合一般的多线程同步场景。通过 synchronized 关键字实现的,具有锁升级机制。
- 显式锁(ReentrantLock):是java.util.concurrent.locks 包中的一种显式锁(也叫可重入锁)。比synchronized 提供更多控制,支持可中断、定时和尝试加锁等功能。并且也是自旋锁:自旋锁是一种非阻塞锁,它通过不断循环检查锁的状态来等待获取锁,而不是让线程进入阻塞状态。
- 读写锁(ReadWriteLock):Java 提供了 ReentrantReadWriteLock类来实现读写锁。适用于读多写少的场景,允许多个读线程并行执行。
- 条件锁(Condition):用于线程之间的协调和通信,配合显式锁使用。Condition 是一个线程同步的工具,通常和ReentrantLock 配合使用,用于在特定条件下使线程等待或唤醒。它类似于 Object.wait() 和 Object.notify(),但提供了更多的功能。
4.synchronized锁升级步骤
锁的状态总共有四种,无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁,但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级,锁升级单向的目的是为了提高获得锁和释放锁的效率。
偏向锁的核心思想是,如果一个线程首次获得了对象,虚拟机将会把对象头标志位设为“01”,那么就进入偏向模式,此时Mark Word 的结构也变为偏向锁结构,同时使用CAS操作把当前对象头Mark Word里存储了当前偏向线程的线程ID。以后当这个线程再次请求同步块时,无需再做任何同步操作,即获取锁。如果偏向锁线程不再活动,则会把对象头设置无锁状态。如果另外一个线程去尝试获取这个锁,偏向锁模式也会宣告结束,锁就会升级成轻量级锁。
当前线程尝试使用 CAS 操作将对象中的 Mark Word 更新为指向Lock Record的指针(实现会在自己的栈帧里面创建Lock Record的备份,用于CAS操作和回滚),如果成功,当前线程获得锁,并且对象Mark Word的锁标志位由“01”转变为“00”,即表示此对象处于轻量级锁状态。如果CAS获取锁失败,代表当前锁有其他线程竞争,此时线程尝试使用自旋来获取资源,自旋是需要消耗CPU的,如果达到自旋最大次数还是没有获取到锁,线程就会处于阻塞状态,锁升级为重量级锁。