- 进程和线程
- 进程作为资源分配的基本单位,线程作为资源调度的基本单位。
- 线程
- 优雅关闭
- 不建议使用stop和destory,会强制杀死线程,资源可能没有释放或正常关闭。
- 合理的是让其运行完。如果是循环运行线程,通过通信机制让其退出。
- 守护线程
- jvm只有守护线程时,会退出。回收线程等都算守护线程。
- 如果有用户线程,就算主线程退出,也不会退出。
- 线程流转图
- 轻量级阻塞(对应线程状态是WAITING(会无期限等待)或者TIMED_WAITING(会阻塞一个有限时间)),可被中断
- 重量级阻塞(对应线程状态是BLOCKED),如synchronized不可被中断。
- 线程流转
-
- 初始化是NEW状态
-
- 调用start()后,进入到RUNNING和READY之间切换,由操作系统根据时间片进行轮转。程序可通过yield()放弃cpu占用
-
- 一旦调用轻量级阻塞函数,线程就会进入到WAITING(会无期限等待)或者TIMED_WAITING(会阻塞一个有限时间)。
-
- 如果用的是重量级阻塞,即synchronized,就会进入BLOCKED状态
- t.interrupted和

- 如何正常退出线程?使用 t.interrupt。准确含义是唤醒轻量级阻塞,而不是
中断一个线程的意思
- interrupt并不会立即使得线程程序马上抛出异常,只有一些声明了抛出InterruptExcetion的才会抛出。本质上相当于给线程发送一个唤醒信号,如果线程恰巧是WAITING或者TIMED_WAITING状态,就会抛出Interrup异常。 如果线程没有被阻塞,则线程什么都不会做。可以通过判断是否收到中断信号来判断。即
isInterrupted
- 哪些声明了?
- 正常使用方法
java while (!Thread.currentThread().isInterrupted() && stat.get() == STAT_RUNNING) { }
- 死锁
- 线程安全
-

- 能不能保证操作的原子性,考虑atomic包下的类够不够我们使用。
- 能不能保证操作的可见性,考虑volatile关键字够不够我们使用
- 如果涉及到对线程的控制(比如一次能使用多少个线程,当前线程触发的条件是否依赖其他线程的结果),考虑CountDownLatch/Semaphore等等。
- 如果是集合,考虑java.util.concurrent包下的集合类。
- 如果synchronized无法满足,考虑lock包下的类
- 原子性, atomic
-
- AtomicLong做累加的时候实际上就是多个线程操作同一个目标资源。在高并发时,只有一个线程是执行成功的,其他的线程都会失败,不断自旋(重试),自旋会成为瓶颈。
- LongAdder,把要操作的目标资源「分散」到数组Cell中。每个线程对自己的 Cell 变量的 value 进行原子操作,大大降低了失败的次数
- 可见性, volatile
- 有序性, volatile
- synchronized 原子性,可见性
- 为什么是重量级锁?需要向操作系统申请资源。早期实现,jdk1.6之后,支持锁升级,即刚开始不需要那么重的锁。
- 锁的对象是什么?锁某个对象。
- 实例方法 —> this对象
- 静态方法 —> Class对象
- 指定对象 —> 指定对象上
- 实现原理(重量级锁)
- 每个对象,都有个monitor对象与之关联。在底层通过monitorenter指令,来获取所有权。如果获取到则可以执行。
- monitor对象组成
- 同步队列: 需要竞争的线程列表。
- 等待队列: 调用wait后,会放到等待队列,此时不能参与竞争。只能等其它线程调用notify或者notifyAll之后才能移到同步队列重新竞争获取。
- 锁升级(无锁 —> 偏向锁(对象头存threadID) —> 轻量级锁(自旋,超过次数转换成重量级锁) —> 重量级锁)
- 偏向锁: 只有1个线程访问时,对象头存threadId,方便后续需要的时候,直接判断是否同一个线程id即可。如果有其它线程竞争,当前线程没执行成功时,需要升级成轻量级锁
- 轻量级锁,将markwork数据拷贝到lockRecord记录中。其它线程竞争时,采用 CAS + 自旋
- 重量级锁: 超过一定次数后,转换到重量级锁。
- 为什么notify/notifyAll/wait等方法存在于顶级对象Object中?
- monitor对象存在于每个Java对象的对象头中(存储的是指针),synchronized锁便是通过这种方式获取锁的,也是为什么Java中任意对象可以作为锁的原因,同时也是notify/notifyAll/wait等方法存在于顶级对象Object中的原因
- synchronized是非公平锁为什么?
- synchronized无论处理哪种锁,都是先尝试获取,获取不到才升级|| 放到队列上的,所以是非公平的
- 偏向锁,如果当前线程ID与markword存储的不相等,则CAS尝试更换线程ID,CAS成功就获取得到锁了
- 轻量级锁实际上也是通过CAS来抢占锁资源(只不过多了拷贝Mark Word到Lock Record的过程),抢占成功到锁就归属给该线程了,但自旋失败一定次数后升级重量级锁
- 重量级锁通过monitor对象中的队列存储线程,但线程进入队列前,还是会先尝试获取得到锁,如果能获取不到才进入线程等待队列中
- CAS compare and swap,比较并交换,对应到CPU指令为cmpxchg。
- 将当前值与内存值进行对比,判断是否有被修改过
- CAS 有三个操作数:当前值A、内存值V、要修改的新值B。
- 假设 当前值A 跟 内存值V 相等,那就将 内存值V 改成B。
- 假设 当前值A 跟 内存值V 不相等,要么就重试,要么就放弃更新
- ABA问题
- AtomicStampedReference, 加个版本号。
资料 §