字符串

字符型常量和字符串常量的区别

字符常量相当于一个整型值(ASCII)值,可以参加表达式运算; 字符串常量代表一个地址值

HashMap

取余(**%**)操作中,如果除数是2的幂次,则等价于与其除数减⼀的与(&)操作(也就是说 hash%length==hash&(length-1)的前提是 length 2 n 次⽅;)

HashMap 和 Hashtable 的区别
初始容量⼤⼩和每次扩充容量⼤⼩的不同 :
① 创建时如果不指定容量初始值, Hashtable默认的初始⼤⼩为 11,之后每次扩充,容量变为原来的 2n+1。 HashMap 默认的初始化⼤⼩为 16。之后每次扩充,容量变为原来的 2 倍。
② 创建时如果给定了容量初始值,那么Hashtable 会直接使⽤你给定的⼤⼩,⽽ HashMap 会将其扩充为 2 的幂次⽅⼤⼩( HashMap 中的 tableSizeFor() ⽅法保证,下⾯给出了源代码)。也就是说 HashMap 总是使⽤ 2 的幂作为哈希表的⼤⼩,后⾯会介绍到为什么是 2 的幂次⽅。

多线程

死锁

  1. public class Main {
  2. private static final Object R1 = new Object();
  3. private static final Object R2 = new Object();
  4. public static void main(String[] args) {
  5. new Thread(() -> {
  6. synchronized (R1) {
  7. System.out.println(Thread.currentThread().getName() + " get R1");
  8. try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
  9. System.out.println(Thread.currentThread().getName() + " try to get R2");
  10. synchronized (R2) {
  11. System.out.println(Thread.currentThread().getName() + " get R2");
  12. }
  13. }
  14. }, "T1").start();
  15. new Thread(() -> {
  16. synchronized (R2) {
  17. System.out.println(Thread.currentThread().getName() + " get R2");
  18. try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
  19. System.out.println(Thread.currentThread().getName() + " try to get R1");
  20. synchronized (R1) {
  21. System.out.println(Thread.currentThread().getName() + " get R1");
  22. }
  23. }
  24. }, "T2").start();
  25. }
  26. }
  1. jconsole
  1. jps
  2. jstack -l <pid>

双检锁

  1. public class Singleton {
  2. private static volatile Singleton instance;
  3. private Singleton() { }
  4. public static Singleton getInstance() {
  5. if (instance == null) {
  6. synchronized (Singleton.class) {
  7. if (instance == null) {
  8. instance = new Singleton();
  9. }
  10. }
  11. }
  12. return instance;
  13. }
  14. }

构造⽅法本身就属于线程安全的,不存在同步的构造⽅法⼀说

synchronized 同步语句块的实现使⽤的是 monitorenter 和 monitorexit 指令,其中
monitorenter 指令指向同步代码块的开始位置, monitorexit 指令则指明同步代码块的结束位
置。
synchronized 修饰的⽅法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是
ACC_SYNCHRONIZED 标识,该标识指明了该⽅法是⼀个同步⽅法。
不过两者的本质都是对对象监视器 monitor 的获取。

JUC 原子类

基本类型 数组类型 引用类型 对象属性修改类型
AtomicInteger AtomicInterArray AtomicReference AtomicIntegerFieldUpdater
AtomicLong AtomicLongArray AtomicStampedReference AtomicLongFieldUpdater
AtomicBoolean AtomicReferedceArray AtomicMarkaleReference AtomicReferenceFieldUpdater

AQS 是⼀个⽤来构建锁和同步器的框架,使⽤ AQS 能简单且⾼效地构造出应⽤⼴泛的⼤量的同
步器,⽐如我们提到的 ReentrantLock , Semaphore ,其他的诸如
ReentrantReadWriteLock , SynchronousQueue , FutureTask 等等皆是基于 AQS 的。当然,我们
⾃⼰也能利⽤ AQS ⾮常轻松容易地构造出符合我们⾃⼰需求的同步器。

AQS 核⼼思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的⼯作线
程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占⽤,那么就需要⼀套线程阻塞
等待以及被唤醒时锁分配的机制,这个机制 AQS 是⽤ CLH 队列锁实现的,即将暂时获取不到锁
的线程加⼊到队列中

AQS 使⽤了模板⽅法模式,⾃定义同步器时需要重写下⾯⼏个 AQS 提供的模板⽅法:

  1. isHeldExclusively()//该线程是否正在独占资源。只有⽤到condition才需要去实现它。
  2. tryAcquire(int)//独占⽅式。尝试获取资源,成功则返回true,失败则返回false。
  3. tryRelease(int)//独占⽅式。尝试释放资源,成功则返回true,失败则返回false。
  4. tryAcquireShared(int)//共享⽅式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可⽤资
  5. 源;正数表示成功,且有剩余资源。
  6. tryReleaseShared(int)//共享⽅式。尝试释放资源,成功则返回true,失败则返回false。

以 ReentrantLock 为例,state 初始化为 0,表示未锁定状态。A 线程 lock()时,会调⽤
tryAcquire()独占该锁并将 state+1。此后,其他线程再 tryAcquire()时就会失败,直到 A 线程
unlock()到 state=0(即释放锁)为⽌,其它线程才有机会获取该锁。当然,释放锁之前,A 线程
⾃⼰是可以重复获取此锁的(state 会累加),这就是可重⼊的概念。但要注意,获取多少次就
要释放多么次,这样才能保证 state 是能回到零态的。

再以 CountDownLatch 以例,任务分为 N 个⼦线程去执⾏,state 也初始化为 N(注意 N 要与线
程个数⼀致)。这 N 个⼦线程是并⾏执⾏的,每个⼦线程执⾏完后 countDown() ⼀次,state 会
CAS(Compare and Swap)减 1。等到所有⼦线程都执⾏完后(即 state=0),会 unpark()主调⽤线
程,然后主调⽤线程就会从 await() 函数返回,继续后余动作。
⼀般来说,⾃定义同步器要么是独占⽅法,要么是共享⽅式,他们也只需实现 tryAcquire
tryRelease 、 tryAcquireShared-tryReleaseShared 中的⼀种即可。但 AQS 也⽀持⾃定义同步器同时
实现独占和共享两种⽅式,如 ReentrantReadWriteLock 。

CompletableFuture

  1. CompletableFuture<Void> task1 = CompletableFuture.supplyAsync(()->{
  2. //⾃定义业务操作
  3. });
  4. CompletableFuture<Void> task6 = CompletableFuture.supplyAsync(()->{
  5. //⾃定义业务操作
  6. });
  7. CompletableFuture<Void> headerFuture = CompletableFuture.allOf(task1, ....., task6);
  8. try {
  9. headerFuture.join();
  10. } catch (Exception ex) {
  11. ......
  12. }
  13. System.out.println("all done. ");

环境变量和启动参数

image.png
image.png
image.png